From 57d04eb131ec9df067028aef278867b4920177c3 Mon Sep 17 00:00:00 2001 From: Vikas Sajjan Date: Mon, 11 Feb 2013 12:58:00 +0200 Subject: usb: xhci: add the suspend/resume functionality Adds power management support to xHCI platform driver. This patch facilitates the transition of xHCI host controller between S0 and S3/S4 power states, during suspend/resume cycles. Signed-off-by: Abhilash Kesavan Signed-off-by: Vikas C Sajjan CC: Doug Anderson Signed-off-by: Felipe Balbi Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-plat.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 51e22bf89505..412fe8d167cf 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -186,11 +186,37 @@ static int xhci_plat_remove(struct platform_device *dev) return 0; } +#ifdef CONFIG_PM +static int xhci_plat_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + return xhci_suspend(xhci); +} + +static int xhci_plat_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + return xhci_resume(xhci, 0); +} + +static const struct dev_pm_ops xhci_plat_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume) +}; +#define DEV_PM_OPS (&xhci_plat_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct platform_driver usb_xhci_driver = { .probe = xhci_plat_probe, .remove = xhci_plat_remove, .driver = { .name = "xhci-hcd", + .pm = DEV_PM_OPS, }, }; MODULE_ALIAS("platform:xhci-hcd"); -- cgit v1.2.3-58-ga151 From eae5b17621d1053c81bec24e5dd5094bf50b31c6 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 2 Apr 2013 08:42:20 -0700 Subject: xhci: Refactor port status into a new function. The hub control function is *way* too long. Refactor it into a new function, and document the side effects of calling that function. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 210 +++++++++++++++++++++++++------------------- 1 file changed, 119 insertions(+), 91 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 1d3545943c50..83fc636fa254 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -534,6 +534,118 @@ void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex) } } +/* + * Converts a raw xHCI port status into the format that external USB 2.0 or USB + * 3.0 hubs use. + * + * Possible side effects: + * - Mark a port as being done with device resume, + * and ring the endpoint doorbells. + * - Stop the Synopsys redriver Compliance Mode polling. + */ +static u32 xhci_get_port_status(struct usb_hcd *hcd, + struct xhci_bus_state *bus_state, + __le32 __iomem **port_array, + u16 wIndex, u32 raw_port_status) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 status = 0; + int slot_id; + + /* wPortChange bits */ + if (raw_port_status & PORT_CSC) + status |= USB_PORT_STAT_C_CONNECTION << 16; + if (raw_port_status & PORT_PEC) + status |= USB_PORT_STAT_C_ENABLE << 16; + if ((raw_port_status & PORT_OCC)) + status |= USB_PORT_STAT_C_OVERCURRENT << 16; + if ((raw_port_status & PORT_RC)) + status |= USB_PORT_STAT_C_RESET << 16; + /* USB3.0 only */ + if (hcd->speed == HCD_USB3) { + if ((raw_port_status & PORT_PLC)) + status |= USB_PORT_STAT_C_LINK_STATE << 16; + if ((raw_port_status & PORT_WRC)) + status |= USB_PORT_STAT_C_BH_RESET << 16; + } + + if (hcd->speed != HCD_USB3) { + if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3 + && (raw_port_status & PORT_POWER)) + status |= USB_PORT_STAT_SUSPEND; + } + if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME && + !DEV_SUPERSPEED(raw_port_status)) { + if ((raw_port_status & PORT_RESET) || + !(raw_port_status & PORT_PE)) + return 0xffffffff; + if (time_after_eq(jiffies, + bus_state->resume_done[wIndex])) { + xhci_dbg(xhci, "Resume USB2 port %d\n", + wIndex + 1); + bus_state->resume_done[wIndex] = 0; + clear_bit(wIndex, &bus_state->resuming_ports); + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); + xhci_dbg(xhci, "set port %d resume\n", + wIndex + 1); + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + wIndex + 1); + if (!slot_id) { + xhci_dbg(xhci, "slot_id is zero\n"); + return 0xffffffff; + } + xhci_ring_device(xhci, slot_id); + bus_state->port_c_suspend |= 1 << wIndex; + bus_state->suspended_ports &= ~(1 << wIndex); + } else { + /* + * The resume has been signaling for less than + * 20ms. Report the port status as SUSPEND, + * let the usbcore check port status again + * and clear resume signaling later. + */ + status |= USB_PORT_STAT_SUSPEND; + } + } + if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 + && (raw_port_status & PORT_POWER) + && (bus_state->suspended_ports & (1 << wIndex))) { + bus_state->suspended_ports &= ~(1 << wIndex); + if (hcd->speed != HCD_USB3) + bus_state->port_c_suspend |= 1 << wIndex; + } + if (raw_port_status & PORT_CONNECT) { + status |= USB_PORT_STAT_CONNECTION; + status |= xhci_port_speed(raw_port_status); + } + if (raw_port_status & PORT_PE) + status |= USB_PORT_STAT_ENABLE; + if (raw_port_status & PORT_OC) + status |= USB_PORT_STAT_OVERCURRENT; + if (raw_port_status & PORT_RESET) + status |= USB_PORT_STAT_RESET; + if (raw_port_status & PORT_POWER) { + if (hcd->speed == HCD_USB3) + status |= USB_SS_PORT_STAT_POWER; + else + status |= USB_PORT_STAT_POWER; + } + /* Update Port Link State for super speed ports*/ + if (hcd->speed == HCD_USB3) { + xhci_hub_report_link_state(&status, raw_port_status); + /* + * Verify if all USB3 Ports Have entered U0 already. + * Delete Compliance Mode Timer if so. + */ + xhci_del_comp_mod_timer(xhci, raw_port_status, wIndex); + } + if (bus_state->port_c_suspend & (1 << wIndex)) + status |= 1 << USB_PORT_FEAT_C_SUSPEND; + + return status; +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { @@ -598,104 +710,20 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (!wIndex || wIndex > max_ports) goto error; wIndex--; - status = 0; temp = xhci_readl(xhci, port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; } - xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); - - /* wPortChange bits */ - if (temp & PORT_CSC) - status |= USB_PORT_STAT_C_CONNECTION << 16; - if (temp & PORT_PEC) - status |= USB_PORT_STAT_C_ENABLE << 16; - if ((temp & PORT_OCC)) - status |= USB_PORT_STAT_C_OVERCURRENT << 16; - if ((temp & PORT_RC)) - status |= USB_PORT_STAT_C_RESET << 16; - /* USB3.0 only */ - if (hcd->speed == HCD_USB3) { - if ((temp & PORT_PLC)) - status |= USB_PORT_STAT_C_LINK_STATE << 16; - if ((temp & PORT_WRC)) - status |= USB_PORT_STAT_C_BH_RESET << 16; - } + status = xhci_get_port_status(hcd, bus_state, port_array, + wIndex, temp); + if (status == 0xffffffff) + goto error; - if (hcd->speed != HCD_USB3) { - if ((temp & PORT_PLS_MASK) == XDEV_U3 - && (temp & PORT_POWER)) - status |= USB_PORT_STAT_SUSPEND; - } - if ((temp & PORT_PLS_MASK) == XDEV_RESUME && - !DEV_SUPERSPEED(temp)) { - if ((temp & PORT_RESET) || !(temp & PORT_PE)) - goto error; - if (time_after_eq(jiffies, - bus_state->resume_done[wIndex])) { - xhci_dbg(xhci, "Resume USB2 port %d\n", - wIndex + 1); - bus_state->resume_done[wIndex] = 0; - clear_bit(wIndex, &bus_state->resuming_ports); - xhci_set_link_state(xhci, port_array, wIndex, - XDEV_U0); - xhci_dbg(xhci, "set port %d resume\n", - wIndex + 1); - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); - if (!slot_id) { - xhci_dbg(xhci, "slot_id is zero\n"); - goto error; - } - xhci_ring_device(xhci, slot_id); - bus_state->port_c_suspend |= 1 << wIndex; - bus_state->suspended_ports &= ~(1 << wIndex); - } else { - /* - * The resume has been signaling for less than - * 20ms. Report the port status as SUSPEND, - * let the usbcore check port status again - * and clear resume signaling later. - */ - status |= USB_PORT_STAT_SUSPEND; - } - } - if ((temp & PORT_PLS_MASK) == XDEV_U0 - && (temp & PORT_POWER) - && (bus_state->suspended_ports & (1 << wIndex))) { - bus_state->suspended_ports &= ~(1 << wIndex); - if (hcd->speed != HCD_USB3) - bus_state->port_c_suspend |= 1 << wIndex; - } - if (temp & PORT_CONNECT) { - status |= USB_PORT_STAT_CONNECTION; - status |= xhci_port_speed(temp); - } - if (temp & PORT_PE) - status |= USB_PORT_STAT_ENABLE; - if (temp & PORT_OC) - status |= USB_PORT_STAT_OVERCURRENT; - if (temp & PORT_RESET) - status |= USB_PORT_STAT_RESET; - if (temp & PORT_POWER) { - if (hcd->speed == HCD_USB3) - status |= USB_SS_PORT_STAT_POWER; - else - status |= USB_PORT_STAT_POWER; - } - /* Update Port Link State for super speed ports*/ - if (hcd->speed == HCD_USB3) { - xhci_hub_report_link_state(&status, temp); - /* - * Verify if all USB3 Ports Have entered U0 already. - * Delete Compliance Mode Timer if so. - */ - xhci_del_comp_mod_timer(xhci, temp, wIndex); - } - if (bus_state->port_c_suspend & (1 << wIndex)) - status |= 1 << USB_PORT_FEAT_C_SUSPEND; + xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", + wIndex, temp); xhci_dbg(xhci, "Get port status returned 0x%x\n", status); + put_unaligned(cpu_to_le32(status), (__le32 *) buf); break; case SetPortFeature: -- cgit v1.2.3-58-ga151 From 063ebeb4335312d05bdf6fb4fc0e41500c6c0afb Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 2 Apr 2013 09:23:42 -0700 Subject: xhci: Report USB 2.1 link status for L1 USB 2.1 devices can go into a lower power link state, L1. When they are active, they are in the L0 state. The L1 transition can be purely driven by software, or some USB host controllers (including some xHCI 1.0 hosts) allow the host hardware to track idleness and automatically place a port into L1. The USB 2.1 Link Power Management ECN gives a way for USB 2.1 hubs that support LPM to report that a port is in L1. The port status bit 5 will be set when the port is in L1. The xHCI host reports the root port as being in 'U2' when the devices is in L1, and as being in 'U0' when the port is active (in L0). Translate the xHCI USB 2.1 link status into the format external hubs use, and pass the L1 status up to the USB core and tools like lsusb. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 83fc636fa254..93f3fdf0ff0a 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -461,8 +461,15 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, } } +/* Updates Link Status for USB 2.1 port */ +static void xhci_hub_report_usb2_link_state(u32 *status, u32 status_reg) +{ + if ((status_reg & PORT_PLS_MASK) == XDEV_U2) + *status |= USB_PORT_STAT_L1; +} + /* Updates Link Status for super Speed port */ -static void xhci_hub_report_link_state(u32 *status, u32 status_reg) +static void xhci_hub_report_usb3_link_state(u32 *status, u32 status_reg) { u32 pls = status_reg & PORT_PLS_MASK; @@ -631,14 +638,16 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, else status |= USB_PORT_STAT_POWER; } - /* Update Port Link State for super speed ports*/ + /* Update Port Link State */ if (hcd->speed == HCD_USB3) { - xhci_hub_report_link_state(&status, raw_port_status); + xhci_hub_report_usb3_link_state(&status, raw_port_status); /* * Verify if all USB3 Ports Have entered U0 already. * Delete Compliance Mode Timer if so. */ xhci_del_comp_mod_timer(xhci, raw_port_status, wIndex); + } else { + xhci_hub_report_usb2_link_state(&status, raw_port_status); } if (bus_state->port_c_suspend & (1 << wIndex)) status |= 1 << USB_PORT_FEAT_C_SUSPEND; -- cgit v1.2.3-58-ga151 From 26b76798e0507429506b93cd49f8c4cfdab06896 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 23 Jul 2013 11:35:47 +0300 Subject: Intel xhci: refactor EHCI/xHCI port switching Make the Linux xHCI driver automatically try to switchover the EHCI ports to xHCI when an Intel xHCI host is detected, and it also finds an Intel EHCI host. This means we will no longer have to add Intel xHCI hosts to a quirks list when the PCI device IDs change. Simply continuing to add new Intel xHCI PCI device IDs to the quirks list is not sustainable. During suspend ports may be swicthed back to EHCI by BIOS and not properly restored to xHCI at resume. Previously both EHCI and xHCI resume functions switched ports back to XHCI, but it's enough to do it in xHCI only because the hub driver doesn't start running again until after both hosts are resumed. Signed-off-by: Mathias Nyman Signed-off-by: Sarah Sharp --- drivers/usb/host/ehci-pci.c | 42 ------------------------------------- drivers/usb/host/pci-quirks.c | 48 ++++++++++++++++--------------------------- drivers/usb/host/pci-quirks.h | 3 +-- drivers/usb/host/xhci-pci.c | 14 +++++++------ 4 files changed, 27 insertions(+), 80 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 595d210655b6..6bd299e61f58 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -315,53 +315,11 @@ done: * Also they depend on separate root hub suspend/resume. */ -static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev) -{ - return pdev->class == PCI_CLASS_SERIAL_USB_EHCI && - pdev->vendor == PCI_VENDOR_ID_INTEL && - (pdev->device == 0x1E26 || - pdev->device == 0x8C2D || - pdev->device == 0x8C26 || - pdev->device == 0x9C26); -} - -static void ehci_enable_xhci_companion(void) -{ - struct pci_dev *companion = NULL; - - /* The xHCI and EHCI controllers are not on the same PCI slot */ - for_each_pci_dev(companion) { - if (!usb_is_intel_switchable_xhci(companion)) - continue; - usb_enable_xhci_ports(companion); - return; - } -} - static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - /* The BIOS on systems with the Intel Panther Point chipset may or may - * not support xHCI natively. That means that during system resume, it - * may switch the ports back to EHCI so that users can use their - * keyboard to select a kernel from GRUB after resume from hibernate. - * - * The BIOS is supposed to remember whether the OS had xHCI ports - * enabled before resume, and switch the ports back to xHCI when the - * BIOS/OS semaphore is written, but we all know we can't trust BIOS - * writers. - * - * Unconditionally switch the ports back to xHCI after a system resume. - * We can't tell whether the EHCI or xHCI controller will be resumed - * first, so we have to do the port switchover in both drivers. Writing - * a '1' to the port switchover registers should have no effect if the - * port was already switched over. - */ - if (usb_is_intel_switchable_ehci(pdev)) - ehci_enable_xhci_companion(); - if (ehci_resume(hcd, hibernated) != 0) (void) ehci_pci_reinit(ehci, pdev); return 0; diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index b9848e4d3d44..2c76ef1320ea 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -735,32 +735,6 @@ static int handshake(void __iomem *ptr, u32 mask, u32 done, return -ETIMEDOUT; } -#define PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI 0x8C31 -#define PCI_DEVICE_ID_INTEL_LYNX_POINT_LP_XHCI 0x9C31 - -bool usb_is_intel_ppt_switchable_xhci(struct pci_dev *pdev) -{ - return pdev->class == PCI_CLASS_SERIAL_USB_XHCI && - pdev->vendor == PCI_VENDOR_ID_INTEL && - pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI; -} - -/* The Intel Lynx Point chipset also has switchable ports. */ -bool usb_is_intel_lpt_switchable_xhci(struct pci_dev *pdev) -{ - return pdev->class == PCI_CLASS_SERIAL_USB_XHCI && - pdev->vendor == PCI_VENDOR_ID_INTEL && - (pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_LP_XHCI); -} - -bool usb_is_intel_switchable_xhci(struct pci_dev *pdev) -{ - return usb_is_intel_ppt_switchable_xhci(pdev) || - usb_is_intel_lpt_switchable_xhci(pdev); -} -EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci); - /* * Intel's Panther Point chipset has two host controllers (EHCI and xHCI) that * share some number of ports. These ports can be switched between either @@ -779,9 +753,23 @@ EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci); * terminations before switching the USB 2.0 wires over, so that USB 3.0 * devices connect at SuperSpeed, rather than at USB 2.0 speeds. */ -void usb_enable_xhci_ports(struct pci_dev *xhci_pdev) +void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) { u32 ports_available; + bool ehci_found = false; + struct pci_dev *companion = NULL; + + /* make sure an intel EHCI controller exists */ + for_each_pci_dev(companion) { + if (companion->class == PCI_CLASS_SERIAL_USB_EHCI && + companion->vendor == PCI_VENDOR_ID_INTEL) { + ehci_found = true; + break; + } + } + + if (!ehci_found) + return; /* Don't switchover the ports if the user hasn't compiled the xHCI * driver. Otherwise they will see "dead" USB ports that don't power @@ -840,7 +828,7 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev) dev_dbg(&xhci_pdev->dev, "USB 2.0 ports that are now switched over " "to xHCI: 0x%x\n", ports_available); } -EXPORT_SYMBOL_GPL(usb_enable_xhci_ports); +EXPORT_SYMBOL_GPL(usb_enable_intel_xhci_ports); void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) { @@ -921,8 +909,8 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev) writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET); hc_init: - if (usb_is_intel_switchable_xhci(pdev)) - usb_enable_xhci_ports(pdev); + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + usb_enable_intel_xhci_ports(pdev); op_reg_base = base + XHCI_HC_LENGTH(readl(base)); diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h index 4b8a2092432f..0a5e0fb8f466 100644 --- a/drivers/usb/host/pci-quirks.h +++ b/drivers/usb/host/pci-quirks.h @@ -8,8 +8,7 @@ int usb_amd_find_chipset_info(void); void usb_amd_dev_put(void); void usb_amd_quirk_pll_disable(void); void usb_amd_quirk_pll_enable(void); -bool usb_is_intel_switchable_xhci(struct pci_dev *pdev); -void usb_enable_xhci_ports(struct pci_dev *xhci_pdev); +void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev); void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); void sb800_prefetch(struct device *dev, int on); #else diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index cc24e39b97d5..475e06e10a77 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -250,13 +250,15 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) * writers. * * Unconditionally switch the ports back to xHCI after a system resume. - * We can't tell whether the EHCI or xHCI controller will be resumed - * first, so we have to do the port switchover in both drivers. Writing - * a '1' to the port switchover registers should have no effect if the - * port was already switched over. + * It should not matter whether the EHCI or xHCI controller is + * resumed first. It's enough to do the switchover in xHCI because + * USB core won't notice anything as the hub driver doesn't start + * running again until after all the devices (including both EHCI and + * xHCI host controllers) have been resumed. */ - if (usb_is_intel_switchable_xhci(pdev)) - usb_enable_xhci_ports(pdev); + + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + usb_enable_intel_xhci_ports(pdev); retval = xhci_resume(xhci, hibernated); return retval; -- cgit v1.2.3-58-ga151 From 03e64e967180181510de06eecae3be44e648b692 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 16 Jul 2013 19:25:59 -0700 Subject: xhci: Correct misplaced newlines Logging messages end in newlines, not have them put in the middle of messages. Signed-off-by: Joe Perches Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 2c49f00260ca..87b5e6581359 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3075,8 +3075,8 @@ static u32 xhci_calculate_no_streams_bitmask(struct xhci_hcd *xhci, /* Are streams already being freed for the endpoint? */ if (ep_state & EP_GETTING_NO_STREAMS) { xhci_warn(xhci, "WARN Can't disable streams for " - "endpoint 0x%x\n, " - "streams are being disabled already.", + "endpoint 0x%x, " + "streams are being disabled already\n", eps[i]->desc.bEndpointAddress); return 0; } @@ -3084,8 +3084,8 @@ static u32 xhci_calculate_no_streams_bitmask(struct xhci_hcd *xhci, if (!(ep_state & EP_HAS_STREAMS) && !(ep_state & EP_GETTING_STREAMS)) { xhci_warn(xhci, "WARN Can't disable streams for " - "endpoint 0x%x\n, " - "streams are already disabled!", + "endpoint 0x%x, " + "streams are already disabled!\n", eps[i]->desc.bEndpointAddress); xhci_warn(xhci, "WARN xhci_free_streams() called " "with non-streams endpoint\n"); @@ -4353,7 +4353,7 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev, state_name, sel); else dev_dbg(&udev->dev, "Device-initiated %s disabled " - "due to long PEL %llu\n ms", + "due to long PEL %llu ms\n", state_name, pel); return USB3_LPM_DISABLED; } -- cgit v1.2.3-58-ga151 From 457163c4c7c69ee7f92c31bd48be332316824a14 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:38 -0700 Subject: USB: ldusb: remove custom dbg_info() macro We want to get rid of CONFIG_USB_DEBUG, so remove the reliance of the ldusb driver on it. Don't use the custom macro, or a special module parameter, instead, rely on the in-kernel dynamic debugging infrastructure, which is much easier to use, and consistant across the whole kernel. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ldusb.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index ac762299eaa8..b1d59532ac22 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -129,19 +129,6 @@ MODULE_DESCRIPTION("LD USB Driver"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("LD USB Devices"); -#ifdef CONFIG_USB_DEBUG - static int debug = 1; -#else - static int debug = 0; -#endif - -/* Use our own dbg macro */ -#define dbg_info(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) - -/* Module parameters */ -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug enabled or not"); - /* All interrupt in transfers are collected in a ring buffer to * avoid racing conditions and get better performance of the driver. */ @@ -256,8 +243,9 @@ static void ld_usb_interrupt_in_callback(struct urb *urb) status == -ESHUTDOWN) { goto exit; } else { - dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", - __func__, status); + dev_dbg(&dev->intf->dev, + "%s: nonzero status received: %d\n", __func__, + status); spin_lock(&dev->rbsl); goto resubmit; /* maybe we can recover */ } @@ -272,8 +260,8 @@ static void ld_usb_interrupt_in_callback(struct urb *urb) *actual_buffer = urb->actual_length; memcpy(actual_buffer+1, dev->interrupt_in_buffer, urb->actual_length); dev->ring_head = next_ring_head; - dbg_info(&dev->intf->dev, "%s: received %d bytes\n", - __func__, urb->actual_length); + dev_dbg(&dev->intf->dev, "%s: received %d bytes\n", + __func__, urb->actual_length); } else { dev_warn(&dev->intf->dev, "Ring buffer overflow, %d bytes dropped\n", @@ -310,9 +298,9 @@ static void ld_usb_interrupt_out_callback(struct urb *urb) if (status && !(status == -ENOENT || status == -ECONNRESET || status == -ESHUTDOWN)) - dbg_info(&dev->intf->dev, - "%s - nonzero write interrupt status received: %d\n", - __func__, status); + dev_dbg(&dev->intf->dev, + "%s - nonzero write interrupt status received: %d\n", + __func__, status); dev->interrupt_out_busy = 0; wake_up_interruptible(&dev->write_wait); @@ -585,7 +573,8 @@ static ssize_t ld_usb_write(struct file *file, const char __user *buffer, bytes_to_write = min(count, write_buffer_size*dev->interrupt_out_endpoint_size); if (bytes_to_write < count) dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write); - dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __func__, count, bytes_to_write); + dev_dbg(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", + __func__, count, bytes_to_write); if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) { retval = -EFAULT; -- cgit v1.2.3-58-ga151 From d26f6e57e71169bab907bdf716021b2d19bd09f0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:39 -0700 Subject: USB: legotower: remove unneeded tracing macros Remove the unneeded tracing macros in this driver. The kernel has a built-in trace function that can be used if this is really still needed. Cc: Juergen Stuber Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 42 +---------------------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 80894791c020..93c95d09f04e 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -318,8 +318,6 @@ static inline void lego_usb_tower_debug_data (int level, const char *function, i */ static inline void tower_delete (struct lego_usb_tower *dev) { - dbg(2, "%s: enter", __func__); - tower_abort_transfers (dev); /* free data structures */ @@ -329,8 +327,6 @@ static inline void tower_delete (struct lego_usb_tower *dev) kfree (dev->interrupt_in_buffer); kfree (dev->interrupt_out_buffer); kfree (dev); - - dbg(2, "%s: leave", __func__); } @@ -346,8 +342,6 @@ static int tower_open (struct inode *inode, struct file *file) struct tower_reset_reply reset_reply; int result; - dbg(2, "%s: enter", __func__); - nonseekable_open(inode, file); subminor = iminor(inode); @@ -435,8 +429,6 @@ unlock_exit: mutex_unlock(&dev->lock); exit: - dbg(2, "%s: leave, return value %d ", __func__, retval); - return retval; } @@ -448,8 +440,6 @@ static int tower_release (struct inode *inode, struct file *file) struct lego_usb_tower *dev; int retval = 0; - dbg(2, "%s: enter", __func__); - dev = file->private_data; if (dev == NULL) { @@ -491,7 +481,6 @@ unlock_exit: exit: mutex_unlock(&open_disc_mutex); exit_nolock: - dbg(2, "%s: leave, return value %d", __func__, retval); return retval; } @@ -502,11 +491,9 @@ exit_nolock: */ static void tower_abort_transfers (struct lego_usb_tower *dev) { - dbg(2, "%s: enter", __func__); - if (dev == NULL) { dbg(1, "%s: dev is null", __func__); - goto exit; + return; } /* shutdown transfer */ @@ -518,9 +505,6 @@ static void tower_abort_transfers (struct lego_usb_tower *dev) } if (dev->interrupt_out_busy && dev->udev) usb_kill_urb(dev->interrupt_out_urb); - -exit: - dbg(2, "%s: leave", __func__); } @@ -553,8 +537,6 @@ static unsigned int tower_poll (struct file *file, poll_table *wait) struct lego_usb_tower *dev; unsigned int mask = 0; - dbg(2, "%s: enter", __func__); - dev = file->private_data; if (!dev->udev) @@ -571,8 +553,6 @@ static unsigned int tower_poll (struct file *file, poll_table *wait) mask |= POLLOUT | POLLWRNORM; } - dbg(2, "%s: leave, mask = %d", __func__, mask); - return mask; } @@ -597,8 +577,6 @@ static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, int retval = 0; unsigned long timeout = 0; - dbg(2, "%s: enter, count = %Zd", __func__, count); - dev = file->private_data; /* lock this object */ @@ -672,7 +650,6 @@ unlock_exit: mutex_unlock(&dev->lock); exit: - dbg(2, "%s: leave, return value %d", __func__, retval); return retval; } @@ -686,8 +663,6 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t size_t bytes_to_write; int retval = 0; - dbg(2, "%s: enter, count = %Zd", __func__, count); - dev = file->private_data; /* lock this object */ @@ -757,8 +732,6 @@ unlock_exit: mutex_unlock(&dev->lock); exit: - dbg(2, "%s: leave, return value %d", __func__, retval); - return retval; } @@ -772,8 +745,6 @@ static void tower_interrupt_in_callback (struct urb *urb) int status = urb->status; int retval; - dbg(4, "%s: enter, status %d", __func__, status); - lego_usb_tower_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); if (status) { @@ -817,7 +788,6 @@ exit: wake_up_interruptible (&dev->read_wait); lego_usb_tower_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); - dbg(4, "%s: leave, status %d", __func__, status); } @@ -829,7 +799,6 @@ static void tower_interrupt_out_callback (struct urb *urb) struct lego_usb_tower *dev = urb->context; int status = urb->status; - dbg(4, "%s: enter, status %d", __func__, status); lego_usb_tower_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); /* sync/async unlink faults aren't errors */ @@ -844,7 +813,6 @@ static void tower_interrupt_out_callback (struct urb *urb) wake_up_interruptible(&dev->write_wait); lego_usb_tower_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); - dbg(4, "%s: leave, status %d", __func__, status); } @@ -866,8 +834,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device int retval = -ENOMEM; int result; - dbg(2, "%s: enter", __func__); - /* allocate memory for our device state and initialize it */ dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL); @@ -993,8 +959,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device exit: - dbg(2, "%s: leave, return value 0x%.8lx (dev)", __func__, (long) dev); - return retval; error: @@ -1013,8 +977,6 @@ static void tower_disconnect (struct usb_interface *interface) struct lego_usb_tower *dev; int minor; - dbg(2, "%s: enter", __func__); - dev = usb_get_intfdata (interface); mutex_lock(&open_disc_mutex); usb_set_intfdata (interface, NULL); @@ -1041,8 +1003,6 @@ static void tower_disconnect (struct usb_interface *interface) dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n", (minor - LEGO_USB_TOWER_MINOR_BASE)); - - dbg(2, "%s: leave", __func__); } module_usb_driver(tower_driver); -- cgit v1.2.3-58-ga151 From fef526cae700471ba37279e41b88e1c3bfdda1b9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:40 -0700 Subject: USB: legousbtower: remove custom debug macro Don't use a custom debug macro for just one driver, instead rely on the in-kernel dynamic debugging logic, which can handle this much better. Cc: Juergen Stuber Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 93c95d09f04e..4a5a8b16d7b5 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -443,7 +443,6 @@ static int tower_release (struct inode *inode, struct file *file) dev = file->private_data; if (dev == NULL) { - dbg(1, "%s: object is NULL", __func__); retval = -ENODEV; goto exit_nolock; } @@ -455,7 +454,8 @@ static int tower_release (struct inode *inode, struct file *file) } if (dev->open_count != 1) { - dbg(1, "%s: device not opened exactly once", __func__); + dev_dbg(&dev->udev->dev, "%s: device not opened exactly once\n", + __func__); retval = -ENODEV; goto unlock_exit; } @@ -491,10 +491,8 @@ exit_nolock: */ static void tower_abort_transfers (struct lego_usb_tower *dev) { - if (dev == NULL) { - dbg(1, "%s: dev is null", __func__); + if (dev == NULL) return; - } /* shutdown transfer */ if (dev->interrupt_in_running) { @@ -594,7 +592,7 @@ static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, /* verify that we actually have some data to read */ if (count == 0) { - dbg(1, "%s: read request of 0 bytes", __func__); + dev_dbg(&dev->udev->dev, "read request of 0 bytes\n"); goto unlock_exit; } @@ -680,7 +678,7 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t /* verify that we actually have some data to write */ if (count == 0) { - dbg(1, "%s: write request of 0 bytes", __func__); + dev_dbg(&dev->udev->dev, "write request of 0 bytes\n"); goto unlock_exit; } @@ -698,7 +696,8 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t /* write the data into interrupt_out_buffer from userspace */ bytes_to_write = min_t(int, count, write_buffer_size); - dbg(4, "%s: count = %Zd, bytes_to_write = %Zd", __func__, count, bytes_to_write); + dev_dbg(&dev->udev->dev, "%s: count = %Zd, bytes_to_write = %Zd\n", + __func__, count, bytes_to_write); if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) { retval = -EFAULT; @@ -753,7 +752,9 @@ static void tower_interrupt_in_callback (struct urb *urb) status == -ESHUTDOWN) { goto exit; } else { - dbg(1, "%s: nonzero status received: %d", __func__, status); + dev_dbg(&dev->udev->dev, + "%s: nonzero status received: %d\n", __func__, + status); goto resubmit; /* maybe we can recover */ } } @@ -766,7 +767,8 @@ static void tower_interrupt_in_callback (struct urb *urb) urb->actual_length); dev->read_buffer_length += urb->actual_length; dev->read_last_arrival = jiffies; - dbg(3, "%s: received %d bytes", __func__, urb->actual_length); + dev_dbg(&dev->udev->dev, "%s: received %d bytes\n", + __func__, urb->actual_length); } else { printk(KERN_WARNING "%s: read_buffer overflow, %d bytes dropped", __func__, urb->actual_length); } @@ -805,8 +807,9 @@ static void tower_interrupt_out_callback (struct urb *urb) if (status && !(status == -ENOENT || status == -ECONNRESET || status == -ESHUTDOWN)) { - dbg(1, "%s - nonzero write bulk status received: %d", - __func__, status); + dev_dbg(&dev->udev->dev, + "%s: nonzero write bulk status received: %d\n", __func__, + status); } dev->interrupt_out_busy = 0; -- cgit v1.2.3-58-ga151 From 4dae99638097b254c863c541368598f5a80e41bf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:41 -0700 Subject: USB: legotower: remove custom debug macro and module parameter Now that we don't use the dbg() macro, remove it, and the module parameter. Also fix up the "dump_data" function to properly use the dynamic debug core and the correct printk options, and don't call it twice per function, as the data doesn't change from the beginning and the end of the call. Cc: Juergen Stuber Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 43 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 34 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 4a5a8b16d7b5..6df99dc4fe87 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -87,28 +87,11 @@ #include -#ifdef CONFIG_USB_DEBUG - static int debug = 4; -#else - static int debug = 0; -#endif - -/* Use our own dbg macro */ -#undef dbg -#define dbg(lvl, format, arg...) \ -do { \ - if (debug >= lvl) \ - printk(KERN_DEBUG "%s: " format "\n", __FILE__, ##arg); \ -} while (0) - /* Version Information */ #define DRIVER_VERSION "v0.96" #define DRIVER_AUTHOR "Juergen Stuber " #define DRIVER_DESC "LEGO USB Tower Driver" -/* Module parameters */ -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug enabled or not"); /* The defaults are chosen to work with the latest versions of leJOS and NQC. */ @@ -298,18 +281,12 @@ static struct usb_driver tower_driver = { /** * lego_usb_tower_debug_data */ -static inline void lego_usb_tower_debug_data (int level, const char *function, int size, const unsigned char *data) +static inline void lego_usb_tower_debug_data(struct device *dev, + const char *function, int size, + const unsigned char *data) { - int i; - - if (debug < level) - return; - - printk (KERN_DEBUG "%s: %s - length = %d, data = ", __FILE__, function, size); - for (i = 0; i < size; ++i) { - printk ("%.2x ", data[i]); - } - printk ("\n"); + dev_dbg(dev, "%s - length = %d, data = %*ph\n", + function, size, size, data); } @@ -744,7 +721,8 @@ static void tower_interrupt_in_callback (struct urb *urb) int status = urb->status; int retval; - lego_usb_tower_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); + lego_usb_tower_debug_data(&dev->udev->dev, __func__, + urb->actual_length, urb->transfer_buffer); if (status) { if (status == -ENOENT || @@ -788,8 +766,6 @@ resubmit: exit: dev->interrupt_in_done = 1; wake_up_interruptible (&dev->read_wait); - - lego_usb_tower_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); } @@ -801,7 +777,8 @@ static void tower_interrupt_out_callback (struct urb *urb) struct lego_usb_tower *dev = urb->context; int status = urb->status; - lego_usb_tower_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); + lego_usb_tower_debug_data(&dev->udev->dev, __func__, + urb->actual_length, urb->transfer_buffer); /* sync/async unlink faults aren't errors */ if (status && !(status == -ENOENT || @@ -814,8 +791,6 @@ static void tower_interrupt_out_callback (struct urb *urb) dev->interrupt_out_busy = 0; wake_up_interruptible(&dev->write_wait); - - lego_usb_tower_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); } -- cgit v1.2.3-58-ga151 From 38726bf5417972ff51b8f047ab4e79f0333f2cf0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:42 -0700 Subject: USB: legotower: remove direct calls to printk() Use the pr_* calls instead, which are much more descriptive. Cc: Juergen Stuber Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 6df99dc4fe87..eb37c9542052 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -75,6 +75,8 @@ * - move reset into open to clean out spurious data */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -325,8 +327,7 @@ static int tower_open (struct inode *inode, struct file *file) interface = usb_find_interface (&tower_driver, subminor); if (!interface) { - printk(KERN_ERR "%s - error, can't find device for minor %d\n", - __func__, subminor); + pr_err("error, can't find device for minor %d\n", subminor); retval = -ENODEV; goto exit; } @@ -563,7 +564,7 @@ static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; - printk(KERN_ERR "legousbtower: No device or device unplugged %d\n", retval); + pr_err("No device or device unplugged %d\n", retval); goto unlock_exit; } @@ -649,7 +650,7 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; - printk(KERN_ERR "legousbtower: No device or device unplugged %d\n", retval); + pr_err("No device or device unplugged %d\n", retval); goto unlock_exit; } @@ -748,7 +749,8 @@ static void tower_interrupt_in_callback (struct urb *urb) dev_dbg(&dev->udev->dev, "%s: received %d bytes\n", __func__, urb->actual_length); } else { - printk(KERN_WARNING "%s: read_buffer overflow, %d bytes dropped", __func__, urb->actual_length); + pr_warn("read_buffer overflow, %d bytes dropped\n", + urb->actual_length); } spin_unlock (&dev->read_buffer_lock); } -- cgit v1.2.3-58-ga151 From 6e42a15803ddbeb3a6f8f64bf599398a0ef5912b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:43 -0700 Subject: USB: adutux: remove unneeded tracing macros Remove the unneeded tracing macros in this driver. The kernel has a built-in trace function that can be used if this is really still needed. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/adutux.c | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index eb3c8c142fa9..e8a9e16789f5 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -147,11 +147,9 @@ static void adu_abort_transfers(struct adu_device *dev) { unsigned long flags; - dbg(2, " %s : enter", __func__); - if (dev->udev == NULL) { dbg(1, " %s : udev is null", __func__); - goto exit; + return; } /* shutdown transfer */ @@ -170,15 +168,10 @@ static void adu_abort_transfers(struct adu_device *dev) usb_kill_urb(dev->interrupt_out_urb); } else spin_unlock_irqrestore(&dev->buflock, flags); - -exit: - dbg(2, " %s : leave", __func__); } static void adu_delete(struct adu_device *dev) { - dbg(2, "%s enter", __func__); - /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -187,8 +180,6 @@ static void adu_delete(struct adu_device *dev) kfree(dev->interrupt_in_buffer); kfree(dev->interrupt_out_buffer); kfree(dev); - - dbg(2, "%s : leave", __func__); } static void adu_interrupt_in_callback(struct urb *urb) @@ -196,7 +187,6 @@ static void adu_interrupt_in_callback(struct urb *urb) struct adu_device *dev = urb->context; int status = urb->status; - dbg(4, " %s : enter, status %d", __func__, status); adu_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); @@ -234,7 +224,6 @@ exit: wake_up_interruptible(&dev->read_wait); adu_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); - dbg(4, " %s : leave, status %d", __func__, status); } static void adu_interrupt_out_callback(struct urb *urb) @@ -242,7 +231,6 @@ static void adu_interrupt_out_callback(struct urb *urb) struct adu_device *dev = urb->context; int status = urb->status; - dbg(4, " %s : enter, status %d", __func__, status); adu_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); if (status != 0) { @@ -262,7 +250,6 @@ exit: adu_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); - dbg(4, " %s : leave, status %d", __func__, status); } static int adu_open(struct inode *inode, struct file *file) @@ -272,8 +259,6 @@ static int adu_open(struct inode *inode, struct file *file) int subminor; int retval; - dbg(2, "%s : enter", __func__); - subminor = iminor(inode); retval = mutex_lock_interruptible(&adutux_mutex); @@ -333,14 +318,11 @@ static int adu_open(struct inode *inode, struct file *file) exit_no_device: mutex_unlock(&adutux_mutex); exit_no_lock: - dbg(2, "%s : leave, return value %d ", __func__, retval); return retval; } static void adu_release_internal(struct adu_device *dev) { - dbg(2, " %s : enter", __func__); - /* decrement our usage count for the device */ --dev->open_count; dbg(2, " %s : open count %d", __func__, dev->open_count); @@ -348,8 +330,6 @@ static void adu_release_internal(struct adu_device *dev) adu_abort_transfers(dev); dev->open_count = 0; } - - dbg(2, " %s : leave", __func__); } static int adu_release(struct inode *inode, struct file *file) @@ -357,8 +337,6 @@ static int adu_release(struct inode *inode, struct file *file) struct adu_device *dev; int retval = 0; - dbg(2, " %s : enter", __func__); - if (file == NULL) { dbg(1, " %s : file is NULL", __func__); retval = -ENODEV; @@ -389,7 +367,6 @@ static int adu_release(struct inode *inode, struct file *file) unlock: mutex_unlock(&adutux_mutex); exit: - dbg(2, " %s : leave, return value %d", __func__, retval); return retval; } @@ -406,11 +383,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, unsigned long flags; DECLARE_WAITQUEUE(wait, current); - dbg(2, " %s : enter, count = %Zd, file=%p", __func__, count, file); - dev = file->private_data; - dbg(2, " %s : dev=%p", __func__, dev); - if (mutex_lock_interruptible(&dev->mtx)) return -ERESTARTSYS; @@ -552,7 +525,6 @@ exit: /* unlock the device */ mutex_unlock(&dev->mtx); - dbg(2, " %s : leave, return value %d", __func__, retval); return retval; } @@ -567,8 +539,6 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, unsigned long flags; int retval; - dbg(2, " %s : enter, count = %Zd", __func__, count); - dev = file->private_data; retval = mutex_lock_interruptible(&dev->mtx); @@ -665,7 +635,6 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, exit: mutex_unlock(&dev->mtx); exit_nolock: - dbg(2, " %s : leave, return value %d", __func__, retval); return retval; exit_onqueue: @@ -711,8 +680,6 @@ static int adu_probe(struct usb_interface *interface, int out_end_size; int i; - dbg(2, " %s : enter", __func__); - if (udev == NULL) { dev_err(&interface->dev, "udev is NULL.\n"); goto exit; @@ -833,8 +800,6 @@ static int adu_probe(struct usb_interface *interface, udev->descriptor.idProduct, dev->serial_number, (dev->minor - ADU_MINOR_BASE)); exit: - dbg(2, " %s : leave, return value %p (dev)", __func__, dev); - return retval; error: @@ -852,8 +817,6 @@ static void adu_disconnect(struct usb_interface *interface) struct adu_device *dev; int minor; - dbg(2, " %s : enter", __func__); - dev = usb_get_intfdata(interface); mutex_lock(&dev->mtx); /* not interruptible */ @@ -874,8 +837,6 @@ static void adu_disconnect(struct usb_interface *interface) dev_info(&interface->dev, "ADU device adutux%d now disconnected\n", (minor - ADU_MINOR_BASE)); - - dbg(2, " %s : leave", __func__); } /* usb specific object needed to register this driver with the usb subsystem */ -- cgit v1.2.3-58-ga151 From 66d4bc30d128e7c7ac4cf64aa78cb76e971cec5b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:44 -0700 Subject: USB: adutux: remove custom debug macro Don't use a custom debug macro for just one driver, instead rely on the in-kernel dynamic debugging logic, which can handle this much better. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/adutux.c | 97 ++++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 39 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index e8a9e16789f5..885f99322a84 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -147,10 +147,8 @@ static void adu_abort_transfers(struct adu_device *dev) { unsigned long flags; - if (dev->udev == NULL) { - dbg(1, " %s : udev is null", __func__); + if (dev->udev == NULL) return; - } /* shutdown transfer */ @@ -195,8 +193,9 @@ static void adu_interrupt_in_callback(struct urb *urb) if (status != 0) { if ((status != -ENOENT) && (status != -ECONNRESET) && (status != -ESHUTDOWN)) { - dbg(1, " %s : nonzero status received: %d", - __func__, status); + dev_dbg(&dev->udev->dev, + "%s : nonzero status received: %d\n", + __func__, status); } goto exit; } @@ -210,10 +209,11 @@ static void adu_interrupt_in_callback(struct urb *urb) dev->interrupt_in_buffer, urb->actual_length); dev->read_buffer_length += urb->actual_length; - dbg(2, " %s reading %d ", __func__, - urb->actual_length); + dev_dbg(&dev->udev->dev,"%s reading %d\n", __func__, + urb->actual_length); } else { - dbg(1, " %s : read_buffer overflow", __func__); + dev_dbg(&dev->udev->dev,"%s : read_buffer overflow\n", + __func__); } } @@ -236,8 +236,9 @@ static void adu_interrupt_out_callback(struct urb *urb) if (status != 0) { if ((status != -ENOENT) && (status != -ECONNRESET)) { - dbg(1, " %s :nonzero status received: %d", - __func__, status); + dev_dbg(&dev->udev->dev, + "%s :nonzero status received: %d\n", __func__, + status); } goto exit; } @@ -262,10 +263,8 @@ static int adu_open(struct inode *inode, struct file *file) subminor = iminor(inode); retval = mutex_lock_interruptible(&adutux_mutex); - if (retval) { - dbg(2, "%s : mutex lock failed", __func__); + if (retval) goto exit_no_lock; - } interface = usb_find_interface(&adu_driver, subminor); if (!interface) { @@ -288,7 +287,8 @@ static int adu_open(struct inode *inode, struct file *file) } ++dev->open_count; - dbg(2, "%s : open count %d", __func__, dev->open_count); + dev_dbg(&dev->udev->dev, "%s: open count %d\n", __func__, + dev->open_count); /* save device in the file's private structure */ file->private_data = dev; @@ -325,7 +325,8 @@ static void adu_release_internal(struct adu_device *dev) { /* decrement our usage count for the device */ --dev->open_count; - dbg(2, " %s : open count %d", __func__, dev->open_count); + dev_dbg(&dev->udev->dev, "%s : open count %d\n", __func__, + dev->open_count); if (dev->open_count <= 0) { adu_abort_transfers(dev); dev->open_count = 0; @@ -338,14 +339,12 @@ static int adu_release(struct inode *inode, struct file *file) int retval = 0; if (file == NULL) { - dbg(1, " %s : file is NULL", __func__); retval = -ENODEV; goto exit; } dev = file->private_data; if (dev == NULL) { - dbg(1, " %s : object is NULL", __func__); retval = -ENODEV; goto exit; } @@ -353,7 +352,7 @@ static int adu_release(struct inode *inode, struct file *file) mutex_lock(&adutux_mutex); /* not interruptible */ if (dev->open_count <= 0) { - dbg(1, " %s : device not opened", __func__); + dev_dbg(&dev->udev->dev, "%s : device not opened\n", __func__); retval = -ENODEV; goto unlock; } @@ -397,17 +396,19 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, /* verify that some data was requested */ if (count == 0) { - dbg(1, " %s : read request of 0 bytes", __func__); + dev_dbg(&dev->udev->dev, "%s : read request of 0 bytes\n", + __func__); goto exit; } timeout = COMMAND_TIMEOUT; - dbg(2, " %s : about to start looping", __func__); + dev_dbg(&dev->udev->dev, "%s : about to start looping\n", __func__); while (bytes_to_read) { int data_in_secondary = dev->secondary_tail - dev->secondary_head; - dbg(2, " %s : while, data_in_secondary=%d, status=%d", - __func__, data_in_secondary, - dev->interrupt_in_urb->status); + dev_dbg(&dev->udev->dev, + "%s : while, data_in_secondary=%d, status=%d\n", + __func__, data_in_secondary, + dev->interrupt_in_urb->status); if (data_in_secondary) { /* drain secondary buffer */ @@ -430,8 +431,9 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, if (dev->read_buffer_length) { /* we secure access to the primary */ char *tmp; - dbg(2, " %s : swap, read_buffer_length = %d", - __func__, dev->read_buffer_length); + dev_dbg(&dev->udev->dev, + "%s : swap, read_buffer_length = %d\n", + __func__, dev->read_buffer_length); tmp = dev->read_buffer_secondary; dev->read_buffer_secondary = dev->read_buffer_primary; dev->read_buffer_primary = tmp; @@ -446,10 +448,14 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, if (!dev->read_urb_finished) { /* somebody is doing IO */ spin_unlock_irqrestore(&dev->buflock, flags); - dbg(2, " %s : submitted already", __func__); + dev_dbg(&dev->udev->dev, + "%s : submitted already\n", + __func__); } else { /* we must initiate input */ - dbg(2, " %s : initiate input", __func__); + dev_dbg(&dev->udev->dev, + "%s : initiate input\n", + __func__); dev->read_urb_finished = 0; spin_unlock_irqrestore(&dev->buflock, flags); @@ -467,7 +473,9 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, if (retval == -ENOMEM) { retval = bytes_read ? bytes_read : -ENOMEM; } - dbg(2, " %s : submit failed", __func__); + dev_dbg(&dev->udev->dev, + "%s : submit failed\n", + __func__); goto exit; } } @@ -486,13 +494,16 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, remove_wait_queue(&dev->read_wait, &wait); if (timeout <= 0) { - dbg(2, " %s : timeout", __func__); + dev_dbg(&dev->udev->dev, + "%s : timeout\n", __func__); retval = bytes_read ? bytes_read : -ETIMEDOUT; goto exit; } if (signal_pending(current)) { - dbg(2, " %s : signal pending", __func__); + dev_dbg(&dev->udev->dev, + "%s : signal pending\n", + __func__); retval = bytes_read ? bytes_read : -EINTR; goto exit; } @@ -555,7 +566,8 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, /* verify that we actually have some data to write */ if (count == 0) { - dbg(1, " %s : write request of 0 bytes", __func__); + dev_dbg(&dev->udev->dev, "%s : write request of 0 bytes\n", + __func__); goto exit; } @@ -568,13 +580,15 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, mutex_unlock(&dev->mtx); if (signal_pending(current)) { - dbg(1, " %s : interrupted", __func__); + dev_dbg(&dev->udev->dev, "%s : interrupted\n", + __func__); set_current_state(TASK_RUNNING); retval = -EINTR; goto exit_onqueue; } if (schedule_timeout(COMMAND_TIMEOUT) == 0) { - dbg(1, "%s - command timed out.", __func__); + dev_dbg(&dev->udev->dev, + "%s - command timed out.\n", __func__); retval = -ETIMEDOUT; goto exit_onqueue; } @@ -585,18 +599,22 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, goto exit_nolock; } - dbg(4, " %s : in progress, count = %Zd", __func__, count); + dev_dbg(&dev->udev->dev, + "%s : in progress, count = %Zd\n", + __func__, count); } else { spin_unlock_irqrestore(&dev->buflock, flags); set_current_state(TASK_RUNNING); remove_wait_queue(&dev->write_wait, &waita); - dbg(4, " %s : sending, count = %Zd", __func__, count); + dev_dbg(&dev->udev->dev, "%s : sending, count = %Zd\n", + __func__, count); /* write the data into interrupt_out_buffer from userspace */ buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); bytes_to_write = count > buffer_size ? buffer_size : count; - dbg(4, " %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", - __func__, buffer_size, count, bytes_to_write); + dev_dbg(&dev->udev->dev, + "%s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd\n", + __func__, buffer_size, count, bytes_to_write); if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) { retval = -EFAULT; @@ -779,7 +797,7 @@ static int adu_probe(struct usb_interface *interface, dev_err(&interface->dev, "Could not retrieve serial number\n"); goto error; } - dbg(2, " %s : serial_number=%s", __func__, dev->serial_number); + dev_dbg(&interface->dev,"serial_number=%s", dev->serial_number); /* we can register the device now, as it is ready */ usb_set_intfdata(interface, dev); @@ -829,7 +847,8 @@ static void adu_disconnect(struct usb_interface *interface) usb_set_intfdata(interface, NULL); /* if the device is not opened, then we clean up right now */ - dbg(2, " %s : open count %d", __func__, dev->open_count); + dev_dbg(&dev->udev->dev, "%s : open count %d\n", + __func__, dev->open_count); if (!dev->open_count) adu_delete(dev); -- cgit v1.2.3-58-ga151 From 1ef37c6047fef8b65d62aa908d9795101b5244d6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:45 -0700 Subject: USB: adutux: remove custom debug macro and module parameter Now that we don't use the dbg() macro, remove it, and the module parameter. Also fix up the "dump_data" function to properly use the dynamic debug core and the correct printk options, and don't call it twice per function, as the data doesn't change from the beginning and the end of the call. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/adutux.c | 50 +++++++++-------------------------------------- 1 file changed, 9 insertions(+), 41 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 885f99322a84..2a4793647580 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -27,30 +27,11 @@ #include #include -#ifdef CONFIG_USB_DEBUG -static int debug = 5; -#else -static int debug = 1; -#endif - -/* Use our own dbg macro */ -#undef dbg -#define dbg(lvl, format, arg...) \ -do { \ - if (debug >= lvl) \ - printk(KERN_DEBUG "%s: " format "\n", __FILE__, ##arg); \ -} while (0) - - /* Version Information */ #define DRIVER_VERSION "v0.0.13" #define DRIVER_AUTHOR "John Homppi" #define DRIVER_DESC "adutux (see www.ontrak.net)" -/* Module parameters */ -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug enabled or not"); - /* Define these values to match your device */ #define ADU_VENDOR_ID 0x0a07 #define ADU_PRODUCT_ID 0x0064 @@ -124,19 +105,11 @@ static DEFINE_MUTEX(adutux_mutex); static struct usb_driver adu_driver; -static void adu_debug_data(int level, const char *function, int size, - const unsigned char *data) +static inline void adu_debug_data(struct device *dev, const char *function, + int size, const unsigned char *data) { - int i; - - if (debug < level) - return; - - printk(KERN_DEBUG "%s: %s - length = %d, data = ", - __FILE__, function, size); - for (i = 0; i < size; ++i) - printk("%.2x ", data[i]); - printk("\n"); + dev_dbg(dev, "%s - length = %d, data = %*ph\n", + function, size, size, data); } /** @@ -185,8 +158,8 @@ static void adu_interrupt_in_callback(struct urb *urb) struct adu_device *dev = urb->context; int status = urb->status; - adu_debug_data(5, __func__, urb->actual_length, - urb->transfer_buffer); + adu_debug_data(&dev->udev->dev, __func__, + urb->actual_length, urb->transfer_buffer); spin_lock(&dev->buflock); @@ -222,8 +195,6 @@ exit: spin_unlock(&dev->buflock); /* always wake up so we recover from errors */ wake_up_interruptible(&dev->read_wait); - adu_debug_data(5, __func__, urb->actual_length, - urb->transfer_buffer); } static void adu_interrupt_out_callback(struct urb *urb) @@ -231,7 +202,8 @@ static void adu_interrupt_out_callback(struct urb *urb) struct adu_device *dev = urb->context; int status = urb->status; - adu_debug_data(5, __func__, urb->actual_length, urb->transfer_buffer); + adu_debug_data(&dev->udev->dev, __func__, + urb->actual_length, urb->transfer_buffer); if (status != 0) { if ((status != -ENOENT) && @@ -240,17 +212,13 @@ static void adu_interrupt_out_callback(struct urb *urb) "%s :nonzero status received: %d\n", __func__, status); } - goto exit; + return; } spin_lock(&dev->buflock); dev->out_urb_finished = 1; wake_up(&dev->write_wait); spin_unlock(&dev->buflock); -exit: - - adu_debug_data(5, __func__, urb->actual_length, - urb->transfer_buffer); } static int adu_open(struct inode *inode, struct file *file) -- cgit v1.2.3-58-ga151 From 28f47c34c75222abefac95a5740b246e065de35f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:46 -0700 Subject: USB: adutux: remove direct calls to printk() Use the pr_* calls instead, which are much more descriptive. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/adutux.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 2a4793647580..7078e9bf0fc0 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -18,6 +18,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -236,8 +238,8 @@ static int adu_open(struct inode *inode, struct file *file) interface = usb_find_interface(&adu_driver, subminor); if (!interface) { - printk(KERN_ERR "adutux: %s - error, can't find device for " - "minor %d\n", __func__, subminor); + pr_err("%s - error, can't find device for minor %d\n", + __func__, subminor); retval = -ENODEV; goto exit_no_device; } @@ -357,8 +359,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; - printk(KERN_ERR "adutux: No device or device unplugged %d\n", - retval); + pr_err("No device or device unplugged %d\n", retval); goto exit; } @@ -527,8 +528,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; - printk(KERN_ERR "adutux: No device or device unplugged %d\n", - retval); + pr_err("No device or device unplugged %d\n", retval); goto exit; } -- cgit v1.2.3-58-ga151 From 4ce10417e840ab58717d2c91986d8684c6858c3f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 26 Jun 2013 16:30:47 -0700 Subject: USB: misc: remove CONFIG_USB_DEBUG from Makefile Now that no usb misc driver is looking for CONFIG_USB_DEBUG, or DEBUG, don't enable it in the Makefile, as that's pointless. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/Makefile | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 3e1bd70b06ea..5a896f319118 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -2,9 +2,6 @@ # Makefile for the rest of the USB drivers # (the ones that don't fit into any other categories) # - -ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG - obj-$(CONFIG_USB_ADUTUX) += adutux.o obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o obj-$(CONFIG_USB_CYPRESS_CY7C63) += cypress_cy7c63.o -- cgit v1.2.3-58-ga151 From a0b9281e29fb8942baddf2ce4067eb611f4f9c01 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:20 +0200 Subject: USB: serial: remove defensive test from set_termios Remove defensive test from set_termios which is never called with a NULL tty. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 3 --- drivers/usb/serial/oti6858.c | 3 --- 2 files changed, 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index d6ef2f8da37d..5627058964d5 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -666,9 +666,6 @@ static void cp210x_set_termios(struct tty_struct *tty, unsigned int bits; unsigned int modem_ctl[4]; - if (!tty) - return; - cflag = tty->termios.c_cflag; old_cflag = old_termios->c_cflag; diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 7e3e0782e51f..cf509c8c1aa1 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -411,9 +411,6 @@ static void oti6858_set_termios(struct tty_struct *tty, __le16 divisor; int br; - if (!tty) - return; - cflag = tty->termios.c_cflag; spin_lock_irqsave(&priv->lock, flags); -- cgit v1.2.3-58-ga151 From 91c4211c394996df485184b7308c8868be1b2055 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:21 +0200 Subject: USB: serial: remove hupping check from tiocmiwait Since commit 31ca020b ("TTY: wake up processes last at hangup") there no longer any need to check the hupping flag in the generic tiocmiwait implementation, so remove it. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index ba45170c78e5..1f31e6b4c251 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -460,12 +460,7 @@ static bool usb_serial_generic_msr_changed(struct tty_struct *tty, /* * Use tty-port initialised flag to detect all hangups including the * one generated at USB-device disconnect. - * - * FIXME: Remove hupping check once tty_port_hangup calls shutdown - * (which clears the initialised flag) before wake up. */ - if (test_bit(TTY_HUPPING, &tty->flags)) - return true; if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) return true; @@ -496,12 +491,8 @@ int usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg) ret = wait_event_interruptible(port->port.delta_msr_wait, usb_serial_generic_msr_changed(tty, arg, &cnow)); - if (!ret) { - if (test_bit(TTY_HUPPING, &tty->flags)) - ret = -EIO; - if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) - ret = -EIO; - } + if (!ret && !test_bit(ASYNCB_INITIALIZED, &port->port.flags)) + ret = -EIO; return ret; } -- cgit v1.2.3-58-ga151 From f5f45304512fa167883dbf7cc23ff92008b173e7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:22 +0200 Subject: USB: serial: clean up dtr_rts Since commit 957dacae ("TTY: fix DTR not being dropped on hang up") dtr_rts is no longer called for uninitialised ports (e.g. after a disconnect), which removes the need to grab the disconnect mutex. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index cb27fcb2fc90..6091bd5a1f4f 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -681,20 +681,10 @@ static int serial_port_carrier_raised(struct tty_port *port) static void serial_port_dtr_rts(struct tty_port *port, int on) { struct usb_serial_port *p = container_of(port, struct usb_serial_port, port); - struct usb_serial *serial = p->serial; - struct usb_serial_driver *drv = serial->type; + struct usb_serial_driver *drv = p->serial->type; - if (!drv->dtr_rts) - return; - /* - * Work-around bug in the tty-layer which can result in dtr_rts - * being called after a disconnect (and tty_unregister_device - * has returned). Remove once bug has been squashed. - */ - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) + if (drv->dtr_rts) drv->dtr_rts(p, on); - mutex_unlock(&serial->disc_mutex); } static const struct tty_port_operations serial_port_ops = { -- cgit v1.2.3-58-ga151 From d7be62211125c85fa1dd796e92aadce84961a502 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:23 +0200 Subject: USB: serial: set drain delay at port probe The port drain delay is constant and should be set at port probe rather than open. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cypress_m8.c | 4 +++- drivers/usb/serial/f81232.c | 3 ++- drivers/usb/serial/io_ti.c | 3 +-- drivers/usb/serial/oti6858.c | 4 +++- drivers/usb/serial/pl2303.c | 3 ++- drivers/usb/serial/spcp8x5.c | 4 ++-- drivers/usb/serial/ti_usb_3410_5052.c | 4 ++-- 7 files changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index e948dc02795d..558605d646f3 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -495,6 +495,8 @@ static int cypress_generic_port_probe(struct usb_serial_port *port) } usb_set_serial_port_data(port, priv); + port->port.drain_delay = 256; + return 0; } @@ -625,7 +627,7 @@ static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) __func__, result); cypress_set_dead(port); } - port->port.drain_delay = 256; + return result; } /* cypress_open */ diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 75e85cbf9e8b..639a18fb67e6 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -207,7 +207,6 @@ static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port) return result; } - port->port.drain_delay = 256; return 0; } @@ -322,6 +321,8 @@ static int f81232_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, priv); + port->port.drain_delay = 256; + return 0; } diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 60054e72b75b..d43adc08283c 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1872,8 +1872,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) ++edge_serial->num_ports_open; - port->port.drain_delay = 1; - goto release_es_lock; unlink_int_urb: @@ -2473,6 +2471,7 @@ static int edge_port_probe(struct usb_serial_port *port) } port->port.closing_wait = msecs_to_jiffies(closing_wait * 10); + port->port.drain_delay = 1; return 0; } diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index cf509c8c1aa1..baa2ea59b921 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -343,6 +343,8 @@ static int oti6858_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, priv); + port->port.drain_delay = 256; /* FIXME: check the FIFO length */ + return 0; } @@ -558,7 +560,7 @@ static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port) /* setup termios */ if (tty) oti6858_set_termios(tty, port, &tmp_termios); - port->port.drain_delay = 256; /* FIXME: check the FIFO length */ + return 0; } diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index cb6bbed374f2..ed9edb2e1849 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -235,6 +235,8 @@ static int pl2303_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, priv); + port->port.drain_delay = 256; + return 0; } @@ -521,7 +523,6 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) return result; } - port->port.drain_delay = 256; return 0; } diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index ddf6c47137dc..4abac28b5992 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -169,6 +169,8 @@ static int spcp8x5_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, priv); + port->port.drain_delay = 256; + return 0; } @@ -411,8 +413,6 @@ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) if (tty) spcp8x5_set_termios(tty, port, NULL); - port->port.drain_delay = 256; - return usb_serial_generic_open(tty, port); } diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 7182bb774b79..18db7b3e52b7 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -441,6 +441,8 @@ static int ti_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, tport); + port->port.drain_delay = 3; + return 0; } @@ -582,8 +584,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) tport->tp_is_open = 1; ++tdev->td_open_port_count; - port->port.drain_delay = 3; - goto release_lock; unlink_int_urb: -- cgit v1.2.3-58-ga151 From 7b94cae47cbfaa1a64cd51b3735eba3abe6af827 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:25 +0200 Subject: USB: console: use dev_dbg Use dev_dbg for debugging. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/console.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index afb50eab2049..bfad2cd39bf3 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -210,10 +210,10 @@ static void usb_console_write(struct console *co, if (count == 0) return; - pr_debug("%s - minor %d, %d byte(s)\n", __func__, port->minor, count); + dev_dbg(&port->dev, "%s - %d byte(s)\n", __func__, count); if (!port->port.console) { - pr_debug("%s - port not opened\n", __func__); + dev_dbg(&port->dev, "%s - port not opened\n", __func__); return; } @@ -234,7 +234,7 @@ static void usb_console_write(struct console *co, retval = serial->type->write(NULL, port, buf, i); else retval = usb_serial_generic_write(NULL, port, buf, i); - pr_debug("%s - return value : %d\n", __func__, retval); + dev_dbg(&port->dev, "%s - write: %d\n", __func__, retval); if (lf) { /* append CR after LF */ unsigned char cr = 13; @@ -244,7 +244,8 @@ static void usb_console_write(struct console *co, else retval = usb_serial_generic_write(NULL, port, &cr, 1); - pr_debug("%s - return value : %d\n", __func__, retval); + dev_dbg(&port->dev, "%s - write cr: %d\n", + __func__, retval); } buf += i; count -= i; -- cgit v1.2.3-58-ga151 From c7b13475b4466adcdcd8378f12b4ee416ef9b98b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:26 +0200 Subject: USB: console: remove unnecessary operations test Remove unnecessary tests for open and write operations as these are set to the generic implementations by usb-serial core if left unset by a subdriver. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/console.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index bfad2cd39bf3..c69bb50d4663 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -151,11 +151,7 @@ static int usb_console_setup(struct console *co, char *options) /* only call the device specific open if this * is the first time the port is opened */ - if (serial->type->open) - retval = serial->type->open(NULL, port); - else - retval = usb_serial_generic_open(NULL, port); - + retval = serial->type->open(NULL, port); if (retval) { dev_err(&port->dev, "could not open USB console port\n"); goto fail; @@ -230,20 +226,12 @@ static void usb_console_write(struct console *co, } /* pass on to the driver specific version of this function if it is available */ - if (serial->type->write) - retval = serial->type->write(NULL, port, buf, i); - else - retval = usb_serial_generic_write(NULL, port, buf, i); + retval = serial->type->write(NULL, port, buf, i); dev_dbg(&port->dev, "%s - write: %d\n", __func__, retval); if (lf) { /* append CR after LF */ unsigned char cr = 13; - if (serial->type->write) - retval = serial->type->write(NULL, - port, &cr, 1); - else - retval = usb_serial_generic_write(NULL, - port, &cr, 1); + retval = serial->type->write(NULL, port, &cr, 1); dev_dbg(&port->dev, "%s - write cr: %d\n", __func__, retval); } -- cgit v1.2.3-58-ga151 From 15e7cead1ed89d445cf86c75a72368e98a7a9039 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:27 +0200 Subject: USB: pl2303: refactor baud-rate handling Break out baud-rate handling from set_termios. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 127 ++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 58 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ed9edb2e1849..c15d64d6e1e7 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -263,68 +263,21 @@ static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value) return retval; } -static void pl2303_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, struct ktermios *old_termios) +static void pl2303_encode_baudrate(struct tty_struct *tty, + struct usb_serial_port *port, + u8 buf[4]) { - struct usb_serial *serial = port->serial; - struct pl2303_serial_private *spriv = usb_get_serial_data(serial); - struct pl2303_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - unsigned int cflag; - unsigned char *buf; - int baud; - int i; - u8 control; const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800, 500000, 614400, 921600, 1228800, 2457600, 3000000, 6000000 }; + + struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + int baud; int baud_floor, baud_ceil; int k; - /* The PL2303 is reported to lose bytes if you change - serial settings even to the same values as before. Thus - we actually need to filter in this specific case */ - - if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) - return; - - cflag = tty->termios.c_cflag; - - buf = kzalloc(7, GFP_KERNEL); - if (!buf) { - dev_err(&port->dev, "%s - out of memory.\n", __func__); - /* Report back no change occurred */ - if (old_termios) - tty->termios = *old_termios; - return; - } - - i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dev_dbg(&port->dev, "0xa1:0x21:0:0 %d - %7ph\n", i, buf); - - if (cflag & CSIZE) { - switch (cflag & CSIZE) { - case CS5: - buf[6] = 5; - break; - case CS6: - buf[6] = 6; - break; - case CS7: - buf[6] = 7; - break; - default: - case CS8: - buf[6] = 8; - break; - } - dev_dbg(&port->dev, "data bits = %d\n", buf[6]); - } - - /* For reference buf[0]:buf[3] baud rate value */ /* NOTE: Only the values defined in baud_sup are supported ! * => if unsupported values are set, the PL2303 seems to use * 9600 baud (at least my PL2303X always does) @@ -378,6 +331,68 @@ static void pl2303_set_termios(struct tty_struct *tty, } } + /* Save resulting baud rate */ + if (baud) + tty_encode_baud_rate(tty, baud, baud); +} + +static void pl2303_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) +{ + struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned int cflag; + unsigned char *buf; + int i; + u8 control; + + /* The PL2303 is reported to lose bytes if you change + serial settings even to the same values as before. Thus + we actually need to filter in this specific case */ + + if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) + return; + + cflag = tty->termios.c_cflag; + + buf = kzalloc(7, GFP_KERNEL); + if (!buf) { + dev_err(&port->dev, "%s - out of memory.\n", __func__); + /* Report back no change occurred */ + if (old_termios) + tty->termios = *old_termios; + return; + } + + i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + dev_dbg(&port->dev, "0xa1:0x21:0:0 %d - %7ph\n", i, buf); + + if (cflag & CSIZE) { + switch (cflag & CSIZE) { + case CS5: + buf[6] = 5; + break; + case CS6: + buf[6] = 6; + break; + case CS7: + buf[6] = 7; + break; + default: + case CS8: + buf[6] = 8; + break; + } + dev_dbg(&port->dev, "data bits = %d\n", buf[6]); + } + + /* For reference buf[0]:buf[3] baud rate value */ + pl2303_encode_baudrate(tty, port, &buf[0]); + /* For reference buf[4]=0 is 1 stop bits */ /* For reference buf[4]=1 is 1.5 stop bits */ /* For reference buf[4]=2 is 2 stop bits */ @@ -461,10 +476,6 @@ static void pl2303_set_termios(struct tty_struct *tty, pl2303_vendor_write(0x0, 0x0, serial); } - /* Save resulting baud rate */ - if (baud) - tty_encode_baud_rate(tty, baud, baud); - kfree(buf); } -- cgit v1.2.3-58-ga151 From b2d6d98fc71bb94ada78d433f26a93498802d3f8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:28 +0200 Subject: USB: pl2303: clean up baud-rate handling Clean up baud-rate handling somewhat. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 90 +++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 49 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index c15d64d6e1e7..dd59c640edcc 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "pl2303.h" /* @@ -275,65 +276,56 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, struct usb_serial *serial = port->serial; struct pl2303_serial_private *spriv = usb_get_serial_data(serial); int baud; - int baud_floor, baud_ceil; - int k; + int i; - /* NOTE: Only the values defined in baud_sup are supported ! + /* + * NOTE: Only the values defined in baud_sup are supported! * => if unsupported values are set, the PL2303 seems to use * 9600 baud (at least my PL2303X always does) */ baud = tty_get_baud_rate(tty); dev_dbg(&port->dev, "baud requested = %d\n", baud); - if (baud) { - /* Set baudrate to nearest supported value */ - for (k=0; k (baud % baud_floor)) - baud = baud_floor; - else - baud = baud_ceil; - } - break; - } - } - if (baud > 1228800) { - /* type_0, type_1 only support up to 1228800 baud */ - if (spriv->type != HX) - baud = 1228800; - else if (baud > 6000000) - baud = 6000000; - } - dev_dbg(&port->dev, "baud set = %d\n", baud); - if (baud <= 115200) { - buf[0] = baud & 0xff; - buf[1] = (baud >> 8) & 0xff; - buf[2] = (baud >> 16) & 0xff; - buf[3] = (baud >> 24) & 0xff; - } else { - /* apparently the formula for higher speeds is: - * baudrate = 12M * 32 / (2^buf[1]) / buf[0] - */ - unsigned tmp = 12*1000*1000*32 / baud; - buf[3] = 0x80; - buf[2] = 0; - buf[1] = (tmp >= 256); - while (tmp >= 256) { - tmp >>= 2; - buf[1] <<= 1; - } - buf[0] = tmp; + if (!baud) + return; + + /* Set baudrate to nearest supported value */ + for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) { + if (baud_sup[i] > baud) + break; + } + + if (i == ARRAY_SIZE(baud_sup)) + baud = baud_sup[i - 1]; + else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1])) + baud = baud_sup[i - 1]; + else + baud = baud_sup[i]; + + /* type_0, type_1 only support up to 1228800 baud */ + if (spriv->type != HX) + baud = max_t(int, baud, 1228800); + + if (baud <= 115200) { + put_unaligned_le32(baud, buf); + } else { + /* + * Apparently the formula for higher speeds is: + * baudrate = 12M * 32 / (2^buf[1]) / buf[0] + */ + unsigned tmp = 12000000 * 32 / baud; + buf[3] = 0x80; + buf[2] = 0; + buf[1] = (tmp >= 256); + while (tmp >= 256) { + tmp >>= 2; + buf[1] <<= 1; } + buf[0] = tmp; } /* Save resulting baud rate */ - if (baud) - tty_encode_baud_rate(tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); + dev_dbg(&port->dev, "baud set = %d\n", baud); } static void pl2303_set_termios(struct tty_struct *tty, -- cgit v1.2.3-58-ga151 From 87265b4514f5a0b79efdccdcb1c6331285652640 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:29 +0200 Subject: USB: pl2303: clean up set_termios Clean up set_termios somewhat. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index dd59c640edcc..91f14c6950f9 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -335,20 +335,18 @@ static void pl2303_set_termios(struct tty_struct *tty, struct pl2303_serial_private *spriv = usb_get_serial_data(serial); struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; - unsigned int cflag; unsigned char *buf; int i; u8 control; - /* The PL2303 is reported to lose bytes if you change - serial settings even to the same values as before. Thus - we actually need to filter in this specific case */ - + /* + * The PL2303 is reported to lose bytes if you change serial settings + * even to the same values as before. Thus we actually need to filter + * in this specific case. + */ if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) return; - cflag = tty->termios.c_cflag; - buf = kzalloc(7, GFP_KERNEL); if (!buf) { dev_err(&port->dev, "%s - out of memory.\n", __func__); @@ -363,8 +361,8 @@ static void pl2303_set_termios(struct tty_struct *tty, 0, 0, buf, 7, 100); dev_dbg(&port->dev, "0xa1:0x21:0:0 %d - %7ph\n", i, buf); - if (cflag & CSIZE) { - switch (cflag & CSIZE) { + if (C_CSIZE(tty)) { + switch (C_CSIZE(tty)) { case CS5: buf[6] = 5; break; @@ -377,7 +375,6 @@ static void pl2303_set_termios(struct tty_struct *tty, default: case CS8: buf[6] = 8; - break; } dev_dbg(&port->dev, "data bits = %d\n", buf[6]); } @@ -388,11 +385,12 @@ static void pl2303_set_termios(struct tty_struct *tty, /* For reference buf[4]=0 is 1 stop bits */ /* For reference buf[4]=1 is 1.5 stop bits */ /* For reference buf[4]=2 is 2 stop bits */ - if (cflag & CSTOPB) { - /* NOTE: Comply with "real" UARTs / RS232: + if (C_CSTOPB(tty)) { + /* + * NOTE: Comply with "real" UARTs / RS232: * use 1.5 instead of 2 stop bits with 5 data bits */ - if ((cflag & CSIZE) == CS5) { + if (C_CSIZE(tty) == CS5) { buf[4] = 1; dev_dbg(&port->dev, "stop bits = 1.5\n"); } else { @@ -404,14 +402,14 @@ static void pl2303_set_termios(struct tty_struct *tty, dev_dbg(&port->dev, "stop bits = 1\n"); } - if (cflag & PARENB) { + if (C_PARENB(tty)) { /* For reference buf[5]=0 is none parity */ /* For reference buf[5]=1 is odd parity */ /* For reference buf[5]=2 is even parity */ /* For reference buf[5]=3 is mark parity */ /* For reference buf[5]=4 is space parity */ - if (cflag & PARODD) { - if (cflag & CMSPAR) { + if (C_PARODD(tty)) { + if (tty->termios.c_cflag & CMSPAR) { buf[5] = 3; dev_dbg(&port->dev, "parity = mark\n"); } else { @@ -419,7 +417,7 @@ static void pl2303_set_termios(struct tty_struct *tty, dev_dbg(&port->dev, "parity = odd\n"); } } else { - if (cflag & CMSPAR) { + if (tty->termios.c_cflag & CMSPAR) { buf[5] = 4; dev_dbg(&port->dev, "parity = space\n"); } else { @@ -440,7 +438,7 @@ static void pl2303_set_termios(struct tty_struct *tty, /* change control lines if we are switching to or from B0 */ spin_lock_irqsave(&priv->lock, flags); control = priv->line_control; - if ((cflag & CBAUD) == B0) + if (C_BAUD(tty) == B0) priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) priv->line_control |= (CONTROL_DTR | CONTROL_RTS); @@ -452,14 +450,13 @@ static void pl2303_set_termios(struct tty_struct *tty, spin_unlock_irqrestore(&priv->lock, flags); } - buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0; - + memset(buf, 0, 7); i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, 0, 0, buf, 7, 100); dev_dbg(&port->dev, "0xa1:0x21:0:0 %d - %7ph\n", i, buf); - if (cflag & CRTSCTS) { + if (C_CRTSCTS(tty)) { if (spriv->type == HX) pl2303_vendor_write(0x0, 0x61, serial); else -- cgit v1.2.3-58-ga151 From 3ba19fe31b726371b45f91a8de9341f5625e75e4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:30 +0200 Subject: USB: pl2303: remove debugging noise Only log the tty_flags in process_read_urb on errors. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 91f14c6950f9..299a0ffcb165 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -790,8 +790,10 @@ static void pl2303_process_read_urb(struct urb *urb) tty_flag = TTY_PARITY; else if (line_status & UART_FRAME_ERROR) tty_flag = TTY_FRAME; - dev_dbg(&port->dev, "%s - tty_flag = %d\n", __func__, tty_flag); + if (tty_flag != TTY_NORMAL) + dev_dbg(&port->dev, "%s - tty_flag = %d\n", __func__, + tty_flag); /* overrun is special, not associated with a char */ if (line_status & UART_OVERRUN_ERROR) tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); -- cgit v1.2.3-58-ga151 From dc43ff924d0d29e53e3f4d26db2c4265b1aa5a2a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:31 +0200 Subject: USB: oti6858: do not call set_termios with uninitialised data Make sure set_termios is not called with uninitialised data at open. The old termios struct is currently not used, but pass NULL instead to avoid future problems (e.g. stack data leak). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/oti6858.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index baa2ea59b921..a2080ac7b7e5 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -508,7 +508,6 @@ static void oti6858_set_termios(struct tty_struct *tty, static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port) { struct oti6858_private *priv = usb_get_serial_port_data(port); - struct ktermios tmp_termios; struct usb_serial *serial = port->serial; struct oti6858_control_pkt *buf; unsigned long flags; @@ -559,7 +558,7 @@ static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port) /* setup termios */ if (tty) - oti6858_set_termios(tty, port, &tmp_termios); + oti6858_set_termios(tty, port, NULL); return 0; } -- cgit v1.2.3-58-ga151 From be0278ccf3c05e7048cc6c7906e41bba0b41b8c7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:32 +0200 Subject: USB: ftdi_sio: clean up device initialisation Do not use zeroed termios data to determine when to unconditionally configure the device at open. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 7260ec660347..abcaeafa525f 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1842,7 +1842,6 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct ktermios dummy; struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1858,10 +1857,8 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) This is same behaviour as serial.c/rs_open() - Kuba */ /* ftdi_set_termios will send usb control messages */ - if (tty) { - memset(&dummy, 0, sizeof(dummy)); - ftdi_set_termios(tty, port, &dummy); - } + if (tty) + ftdi_set_termios(tty, port, NULL); return usb_serial_generic_open(tty, port); } -- cgit v1.2.3-58-ga151 From 3177130fd7827fcf792036eaa54d12156172ed5f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:33 +0200 Subject: USB: ftdi_sio: remove redundant raise of DTR/RTS at open Do not raise DTR/RTS a second time in set_termios at open -- this has already been taken care of by the tty layer. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index abcaeafa525f..86f6dfe87f86 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2187,7 +2187,7 @@ no_data_parity_stop_changes: dev_err(ddev, "%s urb failed to set baudrate\n", __func__); mutex_unlock(&priv->cfg_lock); /* Ensure RTS and DTR are raised when baudrate changed from 0 */ - if (!old_termios || (old_termios->c_cflag & CBAUD) == B0) + if (old_termios && (old_termios->c_cflag & CBAUD) == B0) set_mctrl(port, TIOCM_DTR | TIOCM_RTS); } -- cgit v1.2.3-58-ga151 From e17c1aa2e165556018169ed4dfcc5713167bdb35 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:34 +0200 Subject: USB: ftdi_sio: remove vendor/product module parameters Remove the vendor and product module parameters which were added a long time ago when we did not have the dynamic sysfs interface to add new device ids (and which isn't limited to a single new vid/pid pair). A vid/pid pair can be added dynamically using sysfs, for example: echo 0403 1234 >/sys/bus/usb-serial/drivers/ftdi_sio/new_id Also fix up the in-code comment that got the sysfs path wrong. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 86f6dfe87f86..9bd1f6f1ea80 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -51,8 +51,6 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman , Bill Ryder , Kuba Ober , Andreas Mohr, Johan Hovold " #define DRIVER_DESC "USB FTDI Serial Converters Driver" -static __u16 vendor = FTDI_VID; -static __u16 product; struct ftdi_private { enum ftdi_chip_type chip_type; @@ -144,8 +142,8 @@ static struct ftdi_sio_quirk ftdi_8u2232c_quirk = { /* - * Device ID not listed? Test via module params product/vendor or - * /sys/bus/usb/ftdi_sio/new_id, then send patch/report! + * Device ID not listed? Test it using + * /sys/bus/usb-serial/drivers/ftdi_sio/new_id and send a patch or report. */ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID) }, @@ -881,7 +879,6 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, /* Crucible Devices */ { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) }, - { }, /* Optional parameter entry */ { } /* Terminating entry */ }; @@ -2377,38 +2374,11 @@ static int ftdi_ioctl(struct tty_struct *tty, return -ENOIOCTLCMD; } -static int __init ftdi_init(void) -{ - if (vendor > 0 && product > 0) { - /* Add user specified VID/PID to reserved element of table. */ - int i; - for (i = 0; id_table_combined[i].idVendor; i++) - ; - id_table_combined[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; - id_table_combined[i].idVendor = vendor; - id_table_combined[i].idProduct = product; - } - return usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, id_table_combined); -} - -static void __exit ftdi_exit(void) -{ - usb_serial_deregister_drivers(serial_drivers); -} - - -module_init(ftdi_init); -module_exit(ftdi_exit); +module_usb_serial_driver(serial_drivers, id_table_combined); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -module_param(vendor, ushort, 0); -MODULE_PARM_DESC(vendor, "User specified vendor ID (default=" - __MODULE_STRING(FTDI_VID)")"); -module_param(product, ushort, 0); -MODULE_PARM_DESC(product, "User specified product ID"); - module_param(ndi_latency_timer, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override"); -- cgit v1.2.3-58-ga151 From 2843988299bc503d8a4dc72e032c6be6cd79e28e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:35 +0200 Subject: USB: ftdi_sio: remove unused defines Remove some unused defines. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 9bd1f6f1ea80..bebf3ef7927e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -902,9 +902,6 @@ static const char *ftdi_chip_name[] = { #define FTDI_STATUS_B1_MASK (FTDI_RS_BI) /* End TIOCMIWAIT */ -#define FTDI_IMPL_ASYNC_FLAGS = (ASYNC_SPD_HI | ASYNC_SPD_VHI \ - | ASYNC_SPD_CUST | ASYNC_SPD_SHI | ASYNC_SPD_WARP) - /* function prototypes for a FTDI serial converter */ static int ftdi_sio_probe(struct usb_serial *serial, const struct usb_device_id *id); @@ -971,10 +968,6 @@ static struct usb_serial_driver * const serial_drivers[] = { #define WDR_TIMEOUT 5000 /* default urb timeout */ #define WDR_SHORT_TIMEOUT 1000 /* shorter urb timeout */ -/* High and low are for DTR, RTS etc etc */ -#define HIGH 1 -#define LOW 0 - /* * *************************************************************************** * Utility functions -- cgit v1.2.3-58-ga151 From 421ae6e3db690a5679192aca2463662bc0c1a1da Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:36 +0200 Subject: USB: io_edgeport: remove unused defines Remove unused timeout defines. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_edgeport.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index dc2803b5eb09..c91481d74a14 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -56,9 +56,7 @@ #define MAX_NAME_LEN 64 -#define CHASE_TIMEOUT (5*HZ) /* 5 seconds */ #define OPEN_TIMEOUT (5*HZ) /* 5 seconds */ -#define COMMAND_TIMEOUT (5*HZ) /* 5 seconds */ /* receive port state */ enum RXSTATE { -- cgit v1.2.3-58-ga151 From ddca16e4f10ac83d4d3f02065b3886f85961f7ec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:37 +0200 Subject: USB: io_ti: kill private fifo Kill private write fifo and use the already allocated port write fifo instead. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_ti.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index d43adc08283c..f48e90a222e8 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -64,8 +64,6 @@ #define EDGE_CLOSING_WAIT 4000 /* in .01 sec */ -#define EDGE_OUT_BUF_SIZE 1024 - /* Product information read from the Edgeport */ struct product_info { @@ -93,7 +91,6 @@ struct edgeport_port { spinlock_t ep_lock; int ep_read_urb_state; int ep_write_urb_in_use; - struct kfifo write_fifo; }; struct edgeport_serial { @@ -1903,7 +1900,7 @@ static void edge_close(struct usb_serial_port *port) usb_kill_urb(port->write_urb); edge_port->ep_write_urb_in_use = 0; spin_lock_irqsave(&edge_port->ep_lock, flags); - kfifo_reset_out(&edge_port->write_fifo); + kfifo_reset_out(&port->write_fifo); spin_unlock_irqrestore(&edge_port->ep_lock, flags); dev_dbg(&port->dev, "%s - send umpc_close_port\n", __func__); @@ -1937,7 +1934,7 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, if (edge_port->close_pending == 1) return -ENODEV; - count = kfifo_in_locked(&edge_port->write_fifo, data, count, + count = kfifo_in_locked(&port->write_fifo, data, count, &edge_port->ep_lock); edge_send(port, tty); @@ -1957,7 +1954,7 @@ static void edge_send(struct usb_serial_port *port, struct tty_struct *tty) return; } - count = kfifo_out(&edge_port->write_fifo, + count = kfifo_out(&port->write_fifo, port->write_urb->transfer_buffer, port->bulk_out_size); @@ -2005,7 +2002,7 @@ static int edge_write_room(struct tty_struct *tty) return 0; spin_lock_irqsave(&edge_port->ep_lock, flags); - room = kfifo_avail(&edge_port->write_fifo); + room = kfifo_avail(&port->write_fifo); spin_unlock_irqrestore(&edge_port->ep_lock, flags); dev_dbg(&port->dev, "%s - returns %d\n", __func__, room); @@ -2022,7 +2019,7 @@ static int edge_chars_in_buffer(struct tty_struct *tty) return 0; spin_lock_irqsave(&edge_port->ep_lock, flags); - chars = kfifo_len(&edge_port->write_fifo); + chars = kfifo_len(&port->write_fifo); spin_unlock_irqrestore(&edge_port->ep_lock, flags); dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars); @@ -2449,13 +2446,6 @@ static int edge_port_probe(struct usb_serial_port *port) if (!edge_port) return -ENOMEM; - ret = kfifo_alloc(&edge_port->write_fifo, EDGE_OUT_BUF_SIZE, - GFP_KERNEL); - if (ret) { - kfree(edge_port); - return -ENOMEM; - } - spin_lock_init(&edge_port->ep_lock); edge_port->port = port; edge_port->edge_serial = usb_get_serial_data(port->serial); @@ -2465,7 +2455,6 @@ static int edge_port_probe(struct usb_serial_port *port) ret = edge_create_sysfs_attrs(port); if (ret) { - kfifo_free(&edge_port->write_fifo); kfree(edge_port); return ret; } @@ -2482,7 +2471,6 @@ static int edge_port_remove(struct usb_serial_port *port) edge_port = usb_get_serial_port_data(port); edge_remove_sysfs_attrs(port); - kfifo_free(&edge_port->write_fifo); kfree(edge_port); return 0; -- cgit v1.2.3-58-ga151 From 0fce06da6bb49a3973b7c2ab174761fbc848ca6c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:38 +0200 Subject: USB: io_ti: move port initialisation to probe Move port initialisation code from open to probe where it belongs. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_ti.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index f48e90a222e8..9c18f5985c8d 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1729,22 +1729,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) return -ENODEV; port_number = port->port_number; - switch (port_number) { - case 0: - edge_port->uart_base = UMPMEM_BASE_UART1; - edge_port->dma_address = UMPD_OEDB1_ADDRESS; - break; - case 1: - edge_port->uart_base = UMPMEM_BASE_UART2; - edge_port->dma_address = UMPD_OEDB2_ADDRESS; - break; - default: - dev_err(&port->dev, "Unknown port number!!!\n"); - return -ENODEV; - } - - dev_dbg(&port->dev, "%s - port_number = %d, uart_base = %04x, dma_address = %04x\n", - __func__, port_number, edge_port->uart_base, edge_port->dma_address); dev = port->serial->dev; @@ -2451,18 +2435,40 @@ static int edge_port_probe(struct usb_serial_port *port) edge_port->edge_serial = usb_get_serial_data(port->serial); edge_port->bUartMode = default_uart_mode; + switch (port->port_number) { + case 0: + edge_port->uart_base = UMPMEM_BASE_UART1; + edge_port->dma_address = UMPD_OEDB1_ADDRESS; + break; + case 1: + edge_port->uart_base = UMPMEM_BASE_UART2; + edge_port->dma_address = UMPD_OEDB2_ADDRESS; + break; + default: + dev_err(&port->dev, "unknown port number\n"); + ret = -ENODEV; + goto err; + } + + dev_dbg(&port->dev, + "%s - port_number = %d, uart_base = %04x, dma_address = %04x\n", + __func__, port->port_number, edge_port->uart_base, + edge_port->dma_address); + usb_set_serial_port_data(port, edge_port); ret = edge_create_sysfs_attrs(port); - if (ret) { - kfree(edge_port); - return ret; - } + if (ret) + goto err; port->port.closing_wait = msecs_to_jiffies(closing_wait * 10); port->port.drain_delay = 1; return 0; +err: + kfree(edge_port); + + return ret; } static int edge_port_remove(struct usb_serial_port *port) -- cgit v1.2.3-58-ga151 From 4833960e503549cd73aadc89be5b0c9da0563b5c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:39 +0200 Subject: USB: mos7840: remove broken chase implementation Remove broken "chase" implementation which was supposed to be used to drain the write buffers at break. The chase implementation slept on a wait queue which was never woken up (i.e. no hardware buffers were queried), and thus amounted to nothing more than polling chars_in_buffer, something which has already been taken care of by the tty layer. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7840.c | 48 -------------------------------------------- 1 file changed, 48 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 0a818b238508..dff27e6b677a 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -218,7 +218,6 @@ struct moschip_port { __u8 shadowMCR; /* last MCR value received */ char open; char open_ports; - wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ struct usb_serial_port *port; /* loop back to the owner of this object */ /* Offsets */ @@ -1092,9 +1091,6 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) mos7840_port->read_urb_busy = false; } - /* initialize our wait queues */ - init_waitqueue_head(&mos7840_port->wait_chase); - /* initialize our port settings */ /* Must set to enable ints! */ mos7840_port->shadowMCR = MCR_MASTER_IE; @@ -1211,47 +1207,6 @@ static void mos7840_close(struct usb_serial_port *port) mos7840_port->open = 0; } -/************************************************************************ - * - * mos7840_block_until_chase_response - * - * This function will block the close until one of the following: - * 1. Response to our Chase comes from mos7840 - * 2. A timeout of 10 seconds without activity has expired - * (1K of mos7840 data @ 2400 baud ==> 4 sec to empty) - * - ************************************************************************/ - -static void mos7840_block_until_chase_response(struct tty_struct *tty, - struct moschip_port *mos7840_port) -{ - int timeout = msecs_to_jiffies(1000); - int wait = 10; - int count; - - while (1) { - count = mos7840_chars_in_buffer(tty); - - /* Check for Buffer status */ - if (count <= 0) - return; - - /* Block the thread for a while */ - interruptible_sleep_on_timeout(&mos7840_port->wait_chase, - timeout); - /* No activity.. count down section */ - wait--; - if (wait == 0) { - dev_dbg(&mos7840_port->port->dev, "%s - TIMEOUT\n", __func__); - return; - } else { - /* Reset timeout value back to seconds */ - wait = 10; - } - } - -} - /***************************************************************************** * mos7840_break * this function sends a break to the port @@ -1275,9 +1230,6 @@ static void mos7840_break(struct tty_struct *tty, int break_state) if (mos7840_port == NULL) return; - /* flush and block until tx is empty */ - mos7840_block_until_chase_response(tty, mos7840_port); - if (break_state == -1) data = mos7840_port->shadowLCR | LCR_SET_BREAK; else -- cgit v1.2.3-58-ga151 From 8b61d927beb2479ebff654aef686be4df2d84fc0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:40 +0200 Subject: USB: safe_serial: remove vendor/product module parameters Remove the vendor and product module parameters which were added a long time ago when we did not have the dynamic sysfs interface to add new device ids (and which isn't limited to a single new vid/pid pair). A vid/pid pair can be added dynamically using sysfs, for example: echo 04dd 1234 >/sys/bus/usb-serial/drivers/safe_serial/new_id Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/safe_serial.c | 51 +++++++++++----------------------------- 1 file changed, 14 insertions(+), 37 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 21cd7bf2a8cc..ba895989d8c4 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -92,13 +92,6 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static __u16 vendor; /* no default */ -static __u16 product; /* no default */ -module_param(vendor, ushort, 0); -MODULE_PARM_DESC(vendor, "User specified USB idVendor (required)"); -module_param(product, ushort, 0); -MODULE_PARM_DESC(product, "User specified USB idProduct (required)"); - module_param(safe, bool, 0); MODULE_PARM_DESC(safe, "Turn Safe Encapsulation On/Off"); @@ -140,8 +133,6 @@ static struct usb_device_id id_table[] = { {MY_USB_DEVICE(0x4dd, 0x8003, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */ {MY_USB_DEVICE(0x4dd, 0x8004, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */ {MY_USB_DEVICE(0x5f9, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Sharp tmp */ - /* extra null entry for module vendor/produc parameters */ - {MY_USB_DEVICE(0, 0, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, {} /* terminating entry */ }; @@ -272,7 +263,19 @@ static int safe_prepare_write_buffer(struct usb_serial_port *port, static int safe_startup(struct usb_serial *serial) { - switch (serial->interface->cur_altsetting->desc.bInterfaceProtocol) { + struct usb_interface_descriptor *desc; + + if (serial->dev->descriptor.bDeviceClass != CDC_DEVICE_CLASS) + return -ENODEV; + + desc = &serial->interface->cur_altsetting->desc; + + if (desc->bInterfaceClass != LINEO_INTERFACE_CLASS) + return -ENODEV; + if (desc->bInterfaceSubClass != LINEO_INTERFACE_SUBCLASS_SAFESERIAL) + return -ENODEV; + + switch (desc->bInterfaceProtocol) { case LINEO_SAFESERIAL_CRC: break; case LINEO_SAFESERIAL_CRC_PADDED: @@ -300,30 +303,4 @@ static struct usb_serial_driver * const serial_drivers[] = { &safe_device, NULL }; -static int __init safe_init(void) -{ - int i; - - /* if we have vendor / product parameters patch them into id list */ - if (vendor || product) { - pr_info("vendor: %x product: %x\n", vendor, product); - - for (i = 0; i < ARRAY_SIZE(id_table); i++) { - if (!id_table[i].idVendor && !id_table[i].idProduct) { - id_table[i].idVendor = vendor; - id_table[i].idProduct = product; - break; - } - } - } - - return usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, id_table); -} - -static void __exit safe_exit(void) -{ - usb_serial_deregister_drivers(serial_drivers); -} - -module_init(safe_init); -module_exit(safe_exit); +module_usb_serial_driver(serial_drivers, id_table); -- cgit v1.2.3-58-ga151 From cd1e098b5a57f218322c34d6bf0e2dbea26dc85e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:41 +0200 Subject: USB: ti_usb_3410_5052: kill private fifo Kill private write fifo and use the already allocated port write fifo instead. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ti_usb_3410_5052.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 18db7b3e52b7..d2c6196ca46e 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -45,8 +45,6 @@ #define TI_FIRMWARE_BUF_SIZE 16284 -#define TI_WRITE_BUF_SIZE 1024 - #define TI_TRANSFER_TIMEOUT 2 #define TI_DEFAULT_CLOSING_WAIT 4000 /* in .01 secs */ @@ -77,7 +75,6 @@ struct ti_port { spinlock_t tp_lock; int tp_read_urb_state; int tp_write_urb_in_use; - struct kfifo write_fifo; }; struct ti_device { @@ -431,10 +428,6 @@ static int ti_port_probe(struct usb_serial_port *port) tport->tp_uart_base_addr = TI_UART2_BASE_ADDR; port->port.closing_wait = msecs_to_jiffies(10 * closing_wait); init_waitqueue_head(&tport->tp_write_wait); - if (kfifo_alloc(&tport->write_fifo, TI_WRITE_BUF_SIZE, GFP_KERNEL)) { - kfree(tport); - return -ENOMEM; - } tport->tp_port = port; tport->tp_tdev = usb_get_serial_data(port->serial); tport->tp_uart_mode = 0; /* default is RS232 */ @@ -451,7 +444,6 @@ static int ti_port_remove(struct usb_serial_port *port) struct ti_port *tport; tport = usb_get_serial_port_data(port); - kfifo_free(&tport->write_fifo); kfree(tport); return 0; @@ -616,7 +608,7 @@ static void ti_close(struct usb_serial_port *port) usb_kill_urb(port->write_urb); tport->tp_write_urb_in_use = 0; spin_lock_irqsave(&tport->tp_lock, flags); - kfifo_reset_out(&tport->write_fifo); + kfifo_reset_out(&port->write_fifo); spin_unlock_irqrestore(&tport->tp_lock, flags); port_number = port->port_number; @@ -655,7 +647,7 @@ static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, if (tport == NULL || !tport->tp_is_open) return -ENODEV; - count = kfifo_in_locked(&tport->write_fifo, data, count, + count = kfifo_in_locked(&port->write_fifo, data, count, &tport->tp_lock); ti_send(tport); @@ -674,7 +666,7 @@ static int ti_write_room(struct tty_struct *tty) return 0; spin_lock_irqsave(&tport->tp_lock, flags); - room = kfifo_avail(&tport->write_fifo); + room = kfifo_avail(&port->write_fifo); spin_unlock_irqrestore(&tport->tp_lock, flags); dev_dbg(&port->dev, "%s - returns %d\n", __func__, room); @@ -693,7 +685,7 @@ static int ti_chars_in_buffer(struct tty_struct *tty) return 0; spin_lock_irqsave(&tport->tp_lock, flags); - chars = kfifo_len(&tport->write_fifo); + chars = kfifo_len(&port->write_fifo); spin_unlock_irqrestore(&tport->tp_lock, flags); dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars); @@ -1197,7 +1189,7 @@ static void ti_send(struct ti_port *tport) if (tport->tp_write_urb_in_use) goto unlock; - count = kfifo_out(&tport->write_fifo, + count = kfifo_out(&port->write_fifo, port->write_urb->transfer_buffer, port->bulk_out_size); @@ -1312,7 +1304,7 @@ static int ti_get_serial_info(struct ti_port *tport, ret_serial.line = port->minor; ret_serial.port = port->port_number; ret_serial.flags = tport->tp_flags; - ret_serial.xmit_fifo_size = TI_WRITE_BUF_SIZE; + ret_serial.xmit_fifo_size = kfifo_size(&port->write_fifo); ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800; ret_serial.closing_wait = cwait; -- cgit v1.2.3-58-ga151 From 5df08c8d3fe7ec91b12882b9d49ca50918039e9f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:42 +0200 Subject: USB: ti_usb_3410_5052: remove unused wait queue The write wait queue is never added to since commit f1175daa5 ("USB: ti_usb_3410_5052: kill custom closing_wait"). Remove it completely. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ti_usb_3410_5052.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index d2c6196ca46e..f3e21f5f48f2 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -69,7 +69,6 @@ struct ti_port { __u8 tp_uart_mode; /* 232 or 485 modes */ unsigned int tp_uart_base_addr; int tp_flags; - wait_queue_head_t tp_write_wait; struct ti_device *tp_tdev; struct usb_serial_port *tp_port; spinlock_t tp_lock; @@ -427,7 +426,6 @@ static int ti_port_probe(struct usb_serial_port *port) else tport->tp_uart_base_addr = TI_UART2_BASE_ADDR; port->port.closing_wait = msecs_to_jiffies(10 * closing_wait); - init_waitqueue_head(&tport->tp_write_wait); tport->tp_port = port; tport->tp_tdev = usb_get_serial_data(port->serial); tport->tp_uart_mode = 0; /* default is RS232 */ @@ -1082,13 +1080,11 @@ static void ti_bulk_in_callback(struct urb *urb) case -ESHUTDOWN: dev_dbg(dev, "%s - urb shutting down, %d\n", __func__, status); tport->tp_tdev->td_urb_error = 1; - wake_up_interruptible(&tport->tp_write_wait); return; default: dev_err(dev, "%s - nonzero urb status, %d\n", __func__, status); tport->tp_tdev->td_urb_error = 1; - wake_up_interruptible(&tport->tp_write_wait); } if (status == -EPIPE) @@ -1144,13 +1140,11 @@ static void ti_bulk_out_callback(struct urb *urb) case -ESHUTDOWN: dev_dbg(&port->dev, "%s - urb shutting down, %d\n", __func__, status); tport->tp_tdev->td_urb_error = 1; - wake_up_interruptible(&tport->tp_write_wait); return; default: dev_err_console(port, "%s - nonzero urb status, %d\n", __func__, status); tport->tp_tdev->td_urb_error = 1; - wake_up_interruptible(&tport->tp_write_wait); } /* send any buffered data */ @@ -1224,7 +1218,6 @@ static void ti_send(struct ti_port *tport) /* more room in the buffer for new writes, wakeup */ tty_port_tty_wakeup(&port->port); - wake_up_interruptible(&tport->tp_write_wait); return; unlock: spin_unlock_irqrestore(&tport->tp_lock, flags); -- cgit v1.2.3-58-ga151 From d7ece6515e129a419e6603d49362517a1511425e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Jun 2013 16:47:43 +0200 Subject: USB: ti_usb_3410_5052: remove vendor/product module parameters Remove the vendor and product module parameters which were added a long time ago when we did not have the dynamic sysfs interface to add new device ids (and which isn't limited to five new vid/pid pair). A vid/pid pair can be added dynamically using sysfs, for example: echo 0451 1234 >/sys/bus/usb-serial/drivers/ti_usb_3410_5052_1/new_id for 1-port adapters, or echo 0451 1234 >/sys/bus/usb-serial/drivers/ti_usb_3410_5052_2/new_id for 2-port adapters. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ti_usb_3410_5052.c | 72 ++++------------------------------- 1 file changed, 7 insertions(+), 65 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index f3e21f5f48f2..5585b20397fb 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -141,20 +141,9 @@ static int ti_download_firmware(struct ti_device *tdev); /* module parameters */ static int closing_wait = TI_DEFAULT_CLOSING_WAIT; -static ushort vendor_3410[TI_EXTRA_VID_PID_COUNT]; -static unsigned int vendor_3410_count; -static ushort product_3410[TI_EXTRA_VID_PID_COUNT]; -static unsigned int product_3410_count; -static ushort vendor_5052[TI_EXTRA_VID_PID_COUNT]; -static unsigned int vendor_5052_count; -static ushort product_5052[TI_EXTRA_VID_PID_COUNT]; -static unsigned int product_5052_count; /* supported devices */ -/* the array dimension is the number of default entries plus */ -/* TI_EXTRA_VID_PID_COUNT user defined entries plus 1 terminating */ -/* null entry */ -static struct usb_device_id ti_id_table_3410[15+TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_3410[] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -171,16 +160,18 @@ static struct usb_device_id ti_id_table_3410[15+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STEREO_PLUG_ID) }, { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) }, { USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) }, + { } /* terminator */ }; -static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_5052[] = { { USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, + { } /* terminator */ }; -static struct usb_device_id ti_id_table_combined[19+2*TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_combined[] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -200,7 +191,7 @@ static struct usb_device_id ti_id_table_combined[19+2*TI_EXTRA_VID_PID_COUNT+1] { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) }, { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) }, - { } + { } /* terminator */ }; static struct usb_serial_driver ti_1port_device = { @@ -289,61 +280,12 @@ module_param(closing_wait, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain in close, in .01 secs, default is 4000"); -module_param_array(vendor_3410, ushort, &vendor_3410_count, S_IRUGO); -MODULE_PARM_DESC(vendor_3410, - "Vendor ids for 3410 based devices, 1-5 short integers"); -module_param_array(product_3410, ushort, &product_3410_count, S_IRUGO); -MODULE_PARM_DESC(product_3410, - "Product ids for 3410 based devices, 1-5 short integers"); -module_param_array(vendor_5052, ushort, &vendor_5052_count, S_IRUGO); -MODULE_PARM_DESC(vendor_5052, - "Vendor ids for 5052 based devices, 1-5 short integers"); -module_param_array(product_5052, ushort, &product_5052_count, S_IRUGO); -MODULE_PARM_DESC(product_5052, - "Product ids for 5052 based devices, 1-5 short integers"); - MODULE_DEVICE_TABLE(usb, ti_id_table_combined); +module_usb_serial_driver(serial_drivers, ti_id_table_combined); /* Functions */ -static int __init ti_init(void) -{ - int i, j, c; - - /* insert extra vendor and product ids */ - c = ARRAY_SIZE(ti_id_table_combined) - 2 * TI_EXTRA_VID_PID_COUNT - 1; - j = ARRAY_SIZE(ti_id_table_3410) - TI_EXTRA_VID_PID_COUNT - 1; - for (i = 0; i < min(vendor_3410_count, product_3410_count); i++, j++, c++) { - ti_id_table_3410[j].idVendor = vendor_3410[i]; - ti_id_table_3410[j].idProduct = product_3410[i]; - ti_id_table_3410[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE; - ti_id_table_combined[c].idVendor = vendor_3410[i]; - ti_id_table_combined[c].idProduct = product_3410[i]; - ti_id_table_combined[c].match_flags = USB_DEVICE_ID_MATCH_DEVICE; - } - j = ARRAY_SIZE(ti_id_table_5052) - TI_EXTRA_VID_PID_COUNT - 1; - for (i = 0; i < min(vendor_5052_count, product_5052_count); i++, j++, c++) { - ti_id_table_5052[j].idVendor = vendor_5052[i]; - ti_id_table_5052[j].idProduct = product_5052[i]; - ti_id_table_5052[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE; - ti_id_table_combined[c].idVendor = vendor_5052[i]; - ti_id_table_combined[c].idProduct = product_5052[i]; - ti_id_table_combined[c].match_flags = USB_DEVICE_ID_MATCH_DEVICE; - } - - return usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, ti_id_table_combined); -} - -static void __exit ti_exit(void) -{ - usb_serial_deregister_drivers(serial_drivers); -} - -module_init(ti_init); -module_exit(ti_exit); - - static int ti_startup(struct usb_serial *serial) { struct ti_device *tdev; -- cgit v1.2.3-58-ga151 From f55876dec74edb4d4b3eeaaf1278c2835a4c20f0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:50 -0700 Subject: USB: usbatm: remove unused UDSL_ASSERT macro If this code isn't triggering this assert by now, it never will, so just remove it, it's pointless. Cc: Duncan Sands Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/usbatm.c | 8 -------- drivers/usb/atm/usbatm.h | 12 ------------ 2 files changed, 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 5e0d33a7da58..c76739c60568 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -344,7 +344,6 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char __func__, sarb->len, vcc); /* discard cells already received */ skb_trim(sarb, 0); - UDSL_ASSERT(instance, sarb->tail + ATM_CELL_PAYLOAD <= sarb->end); } memcpy(skb_tail_pointer(sarb), source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); @@ -437,8 +436,6 @@ static void usbatm_extract_cells(struct usbatm_data *instance, unsigned char *cell_buf = instance->cell_buf; unsigned int space_left = stride - buf_usage; - UDSL_ASSERT(instance, buf_usage <= stride); - if (avail_data >= space_left) { /* add new data and process cell */ memcpy(cell_buf + buf_usage, source, space_left); @@ -481,7 +478,6 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance, vdbg(&instance->usb_intf->dev, "%s: skb->len=%d, avail_space=%u", __func__, skb->len, avail_space); - UDSL_ASSERT(instance, !(avail_space % stride)); for (bytes_written = 0; bytes_written < avail_space && ctrl->len; bytes_written += stride, target += stride) { @@ -553,8 +549,6 @@ static void usbatm_rx_process(unsigned long data) if (!urb->iso_frame_desc[i].status) { unsigned int actual_length = urb->iso_frame_desc[i].actual_length; - UDSL_ASSERT(instance, actual_length <= packet_size); - if (!merge_length) merge_start = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; merge_length += actual_length; @@ -1176,8 +1170,6 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, struct urb *urb; unsigned int iso_packets = usb_pipeisoc(channel->endpoint) ? channel->buf_size / channel->packet_size : 0; - UDSL_ASSERT(instance, !usb_pipeisoc(channel->endpoint) || usb_pipein(channel->endpoint)); - urb = usb_alloc_urb(iso_packets, GFP_KERNEL); if (!urb) { dev_err(dev, "%s: no memory for urb %d!\n", __func__, i); diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h index 5fc489405217..a2955b117e88 100644 --- a/drivers/usb/atm/usbatm.h +++ b/drivers/usb/atm/usbatm.h @@ -39,18 +39,6 @@ #define VERBOSE_DEBUG */ -#ifdef DEBUG -#define UDSL_ASSERT(instance, x) BUG_ON(!(x)) -#else -#define UDSL_ASSERT(instance, x) \ - do { \ - if (!(x)) \ - dev_warn(&(instance)->usb_intf->dev, \ - "failed assertion '%s' at line %d", \ - __stringify(x), __LINE__); \ - } while (0) -#endif - #define usb_err(instance, format, arg...) \ dev_err(&(instance)->usb_intf->dev , format , ## arg) #define usb_info(instance, format, arg...) \ -- cgit v1.2.3-58-ga151 From 0fef59d338a7e3384698084dd34d94b295708990 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:51 -0700 Subject: USB: usbatm: remove unneeded trace printk calls We have an in-kernel trace subsystem, so use that instead of printk for trying to figure out what functions are being called. Cc: Duncan Sands Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/usbatm.c | 34 ---------------------------------- 1 file changed, 34 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index c76739c60568..231ef4712e84 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -311,8 +311,6 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char int vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4); u8 pti = ((source[3] & 0xe) >> 1); - vdbg(&instance->usb_intf->dev, "%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti); - if ((vci != instance->cached_vci) || (vpi != instance->cached_vpi)) { instance->cached_vpi = vpi; instance->cached_vci = vci; @@ -476,9 +474,6 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance, unsigned int bytes_written; unsigned int stride = instance->tx_channel.stride; - vdbg(&instance->usb_intf->dev, "%s: skb->len=%d, avail_space=%u", - __func__, skb->len, avail_space); - for (bytes_written = 0; bytes_written < avail_space && ctrl->len; bytes_written += stride, target += stride) { unsigned int data_len = min_t(unsigned int, skb->len, ATM_CELL_PAYLOAD); @@ -639,7 +634,6 @@ static void usbatm_cancel_send(struct usbatm_data *instance, { struct sk_buff *skb, *n; - atm_dbg(instance, "%s entered\n", __func__); spin_lock_irq(&instance->sndqueue.lock); skb_queue_walk_safe(&instance->sndqueue, skb, n) { if (UDSL_SKB(skb)->atm.vcc == vcc) { @@ -657,7 +651,6 @@ static void usbatm_cancel_send(struct usbatm_data *instance, usbatm_pop(vcc, skb); } tasklet_enable(&instance->tx_channel.tasklet); - atm_dbg(instance, "%s done\n", __func__); } static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) @@ -675,9 +668,6 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) goto fail; } - vdbg(&instance->usb_intf->dev, "%s called (skb 0x%p, len %u)", __func__, - skb, skb->len); - if (vcc->qos.aal != ATM_AAL5) { atm_rldbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal); err = -EINVAL; @@ -717,8 +707,6 @@ static void usbatm_destroy_instance(struct kref *kref) { struct usbatm_data *instance = container_of(kref, struct usbatm_data, refcount); - usb_dbg(instance, "%s\n", __func__); - tasklet_kill(&instance->rx_channel.tasklet); tasklet_kill(&instance->tx_channel.tasklet); usb_put_dev(instance->usb_dev); @@ -727,15 +715,11 @@ static void usbatm_destroy_instance(struct kref *kref) static void usbatm_get_instance(struct usbatm_data *instance) { - usb_dbg(instance, "%s\n", __func__); - kref_get(&instance->refcount); } static void usbatm_put_instance(struct usbatm_data *instance) { - usb_dbg(instance, "%s\n", __func__); - kref_put(&instance->refcount, usbatm_destroy_instance); } @@ -751,7 +735,6 @@ static void usbatm_atm_dev_close(struct atm_dev *atm_dev) if (!instance) return; - usb_dbg(instance, "%s\n", __func__); atm_dev->dev_data = NULL; /* catch bugs */ usbatm_put_instance(instance); /* taken in usbatm_atm_init */ } @@ -807,8 +790,6 @@ static int usbatm_atm_open(struct atm_vcc *vcc) if (!instance) return -ENODEV; - atm_dbg(instance, "%s: vpi %hd, vci %d\n", __func__, vpi, vci); - /* only support AAL5 */ if ((vcc->qos.aal != ATM_AAL5)) { atm_warn(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal); @@ -885,11 +866,6 @@ static void usbatm_atm_close(struct atm_vcc *vcc) if (!instance || !vcc_data) return; - atm_dbg(instance, "%s entered\n", __func__); - - atm_dbg(instance, "%s: deallocating vcc 0x%p with vpi %d vci %d\n", - __func__, vcc_data, vcc_data->vpi, vcc_data->vci); - usbatm_cancel_send(instance, vcc); mutex_lock(&instance->serialize); /* vs self, usbatm_atm_open, usbatm_usb_disconnect */ @@ -916,8 +892,6 @@ static void usbatm_atm_close(struct atm_vcc *vcc) clear_bit(ATM_VF_ADDR, &vcc->flags); mutex_unlock(&instance->serialize); - - atm_dbg(instance, "%s successful\n", __func__); } static int usbatm_atm_ioctl(struct atm_dev *atm_dev, unsigned int cmd, @@ -1054,12 +1028,6 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, int i, length; unsigned int maxpacket, num_packets; - dev_dbg(dev, "%s: trying driver %s with vendor=%04x, product=%04x, ifnum %2d\n", - __func__, driver->driver_name, - le16_to_cpu(usb_dev->descriptor.idVendor), - le16_to_cpu(usb_dev->descriptor.idProduct), - intf->altsetting->desc.bInterfaceNumber); - /* instance init */ instance = kzalloc(sizeof(*instance) + sizeof(struct urb *) * (num_rcv_urbs + num_snd_urbs), GFP_KERNEL); if (!instance) { @@ -1258,8 +1226,6 @@ void usbatm_usb_disconnect(struct usb_interface *intf) struct usbatm_vcc_data *vcc_data; int i; - dev_dbg(dev, "%s entered\n", __func__); - if (!instance) { dev_dbg(dev, "%s: NULL instance!\n", __func__); return; -- cgit v1.2.3-58-ga151 From ca4d701323f8450b1bf80359a41af8cbccc7731a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:52 -0700 Subject: USB: usbatm: don't rely on CONFIG_USB_DEBUG Enable the USB atm drivers to use the dynamic debugging subsystem, and not rely on if CONFIG_USB_DEBUG is enabled or not for debugging messages. This also provides a saner debug message, pointing out the exact device the message is coming from. This also means the drivers do not have to be rebuilt to get debugging messages, important for getting information from users who can not rebuild their kernels. Cc: Duncan Sands Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/usbatm.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h index a2955b117e88..71dd13994bbe 100644 --- a/drivers/usb/atm/usbatm.h +++ b/drivers/usb/atm/usbatm.h @@ -45,13 +45,8 @@ dev_info(&(instance)->usb_intf->dev , format , ## arg) #define usb_warn(instance, format, arg...) \ dev_warn(&(instance)->usb_intf->dev , format , ## arg) -#ifdef DEBUG -#define usb_dbg(instance, format, arg...) \ - dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg) -#else #define usb_dbg(instance, format, arg...) \ - do {} while (0) -#endif + dev_dbg(&(instance)->usb_intf->dev , format , ## arg) /* FIXME: move to dev_* once ATM is driver model aware */ #define atm_printk(level, instance, format, arg...) \ -- cgit v1.2.3-58-ga151 From 2d6401cf4ca3861692a4779745e0049cac769d10 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:53 -0700 Subject: USB: usbatm: move the atm_dbg() call to use dynamic debug Move the atm_dbg() call to use the dynamic debug subsystem, and not rely on CONFIG_USB_DEBUG for if things should be printed out or not. This also means the drivers do not have to be rebuilt to get debugging messages, important for getting information from users who can not rebuild their kernels. Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/usbatm.h | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h index 71dd13994bbe..5651231a7437 100644 --- a/drivers/usb/atm/usbatm.h +++ b/drivers/usb/atm/usbatm.h @@ -59,18 +59,12 @@ atm_printk(KERN_INFO, instance , format , ## arg) #define atm_warn(instance, format, arg...) \ atm_printk(KERN_WARNING, instance , format , ## arg) -#ifdef DEBUG -#define atm_dbg(instance, format, arg...) \ - atm_printk(KERN_DEBUG, instance , format , ## arg) -#define atm_rldbg(instance, format, arg...) \ +#define atm_dbg(instance, format, arg...) \ + dynamic_pr_debug("ATM dev %d: " format , \ + (instance)->atm_dev->number , ## arg) +#define atm_rldbg(instance, format, arg...) \ if (printk_ratelimit()) \ - atm_printk(KERN_DEBUG, instance , format , ## arg) -#else -#define atm_dbg(instance, format, arg...) \ - do {} while (0) -#define atm_rldbg(instance, format, arg...) \ - do {} while (0) -#endif + atm_dbg(instance , format , ## arg) /* flags, set by mini-driver in bind() */ -- cgit v1.2.3-58-ga151 From 2c21c985f1bc9f9b19ce258f38bb400ced3c7309 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:54 -0700 Subject: USB: usbatm: remove CONFIG_USB_DEBUG dependancy Now that no USB atm driver is relying on the CONFIG_USB_DEBUG option (well, really the DEBUG option, thanks to some Makefile fun), remove it from the Makefile. Also remove two last vestiges of DEBUG in the usbatm.c driver, moving one to VERBOSE_DEBUG, which no one ever really cares about, and the other to use the dynamic debug subsystem. Cc: Duncan Sands Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/Makefile | 3 --- drivers/usb/atm/usbatm.c | 7 +++---- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile index a5d792ec3ad5..ac278946b06c 100644 --- a/drivers/usb/atm/Makefile +++ b/drivers/usb/atm/Makefile @@ -1,9 +1,6 @@ # # Makefile for USB ATM/xDSL drivers # - -ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG - obj-$(CONFIG_USB_CXACRU) += cxacru.o obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o obj-$(CONFIG_USB_UEAGLEATM) += ueagle-atm.o diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 231ef4712e84..25a7bfcf666c 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -661,7 +661,7 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) /* racy disconnection check - fine */ if (!instance || instance->disconnected) { -#ifdef DEBUG +#ifdef VERBOSE_DEBUG printk_ratelimited(KERN_DEBUG "%s: %s!\n", __func__, instance ? "disconnected" : "NULL instance"); #endif err = -ENODEV; @@ -1120,14 +1120,13 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, instance->rx_channel.buf_size = num_packets * maxpacket; instance->rx_channel.packet_size = maxpacket; -#ifdef DEBUG for (i = 0; i < 2; i++) { struct usbatm_channel *channel = i ? &instance->tx_channel : &instance->rx_channel; - dev_dbg(dev, "%s: using %d byte buffer for %s channel 0x%p\n", __func__, channel->buf_size, i ? "tx" : "rx", channel); + dev_dbg(dev, "%s: using %d byte buffer for %s channel 0x%p\n", + __func__, channel->buf_size, i ? "tx" : "rx", channel); } -#endif /* initialize urbs */ -- cgit v1.2.3-58-ga151 From 523e531ea694eadf2957ee207bb4e0681e837057 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:55 -0700 Subject: USB: phy: remove custom DBG macro Use the in-kernel pr_debug() calls instead of trying to roll your own DBG macro. This means the dynamic debugging calls now work here, and there is no dependency on CONFIG_USB_DEBUG for the phy code anymore. Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-fsl-usb.c | 6 +++--- drivers/usb/phy/phy-fsm-usb.h | 11 ++--------- drivers/usb/phy/phy-isp1301-omap.c | 6 +----- 3 files changed, 6 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index e771bafb9f1d..181bfb10a57a 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -611,7 +611,7 @@ static int fsl_otg_set_peripheral(struct usb_otg *otg, otg_dev->fsm.b_bus_req = 1; /* start the gadget right away if the ID pin says Mini-B */ - DBG("ID pin=%d\n", otg_dev->fsm.id); + pr_debug("ID pin=%d\n", otg_dev->fsm.id); if (otg_dev->fsm.id == 1) { fsl_otg_start_host(&otg_dev->fsm, 0); otg_drv_vbus(&otg_dev->fsm, 0); @@ -684,7 +684,7 @@ static int fsl_otg_start_hnp(struct usb_otg *otg) if (otg_dev != fsl_otg_dev) return -ENODEV; - DBG("start_hnp...n"); + pr_debug("start_hnp...\n"); /* clear a_bus_req to enter a_suspend state */ otg_dev->fsm.a_bus_req = 0; @@ -941,7 +941,7 @@ int usb_otg_start(struct platform_device *pdev) p_otg->fsm.id = 0; } - DBG("initial ID pin=%d\n", p_otg->fsm.id); + pr_debug("initial ID pin=%d\n", p_otg->fsm.id); /* enable OTG ID pin interrupt */ temp = fsl_readl(&p_otg->dr_mem_map->otgsc); diff --git a/drivers/usb/phy/phy-fsm-usb.h b/drivers/usb/phy/phy-fsm-usb.h index c30a2e1d9e46..fbe586206f33 100644 --- a/drivers/usb/phy/phy-fsm-usb.h +++ b/drivers/usb/phy/phy-fsm-usb.h @@ -15,18 +15,11 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#undef DEBUG #undef VERBOSE -#ifdef DEBUG -#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt , \ - __func__, ## args) -#else -#define DBG(fmt, args...) do {} while (0) -#endif - #ifdef VERBOSE -#define VDBG DBG +#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \ + __func__, ## args) #else #define VDBG(stuff...) do {} while (0) #endif diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index ae481afcb3ec..d3a5160e4cc7 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -40,9 +40,7 @@ #include -#ifndef DEBUG -#undef VERBOSE -#endif +#undef VERBOSE #define DRIVER_VERSION "24 August 2004" @@ -387,7 +385,6 @@ static void b_idle(struct isp1301 *isp, const char *tag) static void dump_regs(struct isp1301 *isp, const char *label) { -#ifdef DEBUG u8 ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1); u8 status = isp1301_get_u8(isp, ISP1301_OTG_STATUS); u8 src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); @@ -396,7 +393,6 @@ dump_regs(struct isp1301 *isp, const char *label) omap_readl(OTG_CTRL), label, state_name(isp), ctrl, status, src); /* mode control and irq enables don't change much */ -#endif } /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-58-ga151 From 8fb822c5077c30258f27308d55f0eda5e398f3b6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:56 -0700 Subject: USB: phy: remove CONFIG_USB_DEBUG usage Now that no USB phy driver is using CONFIG_USB_DEBUG, remove it from the Makefile. Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/Makefile | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 070eca3af18b..98730ca1d5d3 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -1,9 +1,6 @@ # # Makefile for physical layer USB drivers # - -ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG - obj-$(CONFIG_USB_PHY) += phy.o obj-$(CONFIG_OF) += of.o -- cgit v1.2.3-58-ga151 From 0667fe76764927d986265e7405d8f4a4025f7d23 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:57 -0700 Subject: USB: gadget: fix up comment This endif is for CONFIG_USB_GADGET_DEBUG_FILES, not CONFIG_USB_DEBUG, so document it properly. Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/storage_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index dbce3a9074e6..8c71212a8c52 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -172,7 +172,7 @@ MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers"); */ #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS -#endif /* CONFIG_USB_DEBUG */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /* check if fsg_num_buffers is within a valid range */ static inline int fsg_num_buffers_validate(void) -- cgit v1.2.3-58-ga151 From 051a0689d64348264fb6e32736be283398b3f0c3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:58 -0700 Subject: USB: isp1362: remove unused _BUG_ON() calls We shouldn't ever panic in a driver, and these calls were never being used, so just delete them, as obviously the driver is working properly now (right?) Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 2 -- drivers/usb/host/isp1362.h | 20 -------------------- 2 files changed, 22 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index b04e8ece4d35..c747ba35f731 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -350,8 +350,6 @@ static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep struct ptd *ptd = &ep->ptd; int len = PTD_GET_DIR(ptd) == PTD_DIR_IN ? 0 : ep->length; - _BUG_ON(ep->ptd_offset < 0); - prefetch(ptd); isp1362_write_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE); if (len) diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index 0f97820e65be..4ef746dcef85 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -91,7 +91,6 @@ typedef const unsigned int isp1362_reg_t; #define ISP1362_REG_NO(r) ((r) & REG_NO_MASK) -#define _BUG_ON(x) BUG_ON(x) #define _WARN_ON(x) WARN_ON(x) #define ISP1362_REG(name, addr, width, rw) \ @@ -102,7 +101,6 @@ static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw)) #else typedef const unsigned char isp1362_reg_t; #define ISP1362_REG_NO(r) (r) -#define _BUG_ON(x) do {} while (0) #define _WARN_ON(x) do {} while (0) #define ISP1362_REG(name, addr, width, rw) \ @@ -645,9 +643,7 @@ static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd */ static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg) { - /*_BUG_ON((reg & ISP1362_REG_WRITE_OFFSET) && !(reg & REG_ACCESS_W));*/ REG_ACCESS_TEST(reg); - _BUG_ON(!irqs_disabled()); DUMMY_DELAY_ACCESS; writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg); DUMMY_DELAY_ACCESS; @@ -656,7 +652,6 @@ static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t re static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val) { - _BUG_ON(!irqs_disabled()); DUMMY_DELAY_ACCESS; writew(val, isp1362_hcd->data_reg); } @@ -665,7 +660,6 @@ static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd) { u16 val; - _BUG_ON(!irqs_disabled()); DUMMY_DELAY_ACCESS; val = readw(isp1362_hcd->data_reg); @@ -674,7 +668,6 @@ static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd) static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val) { - _BUG_ON(!irqs_disabled()); #if USE_32BIT DUMMY_DELAY_ACCESS; writel(val, isp1362_hcd->data_reg); @@ -690,7 +683,6 @@ static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd) { u32 val; - _BUG_ON(!irqs_disabled()); #if USE_32BIT DUMMY_DELAY_ACCESS; val = readl(isp1362_hcd->data_reg); @@ -713,8 +705,6 @@ static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 le if (!len) return; - _BUG_ON(!irqs_disabled()); - RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf); #if USE_32BIT if (len >= 4) { @@ -760,8 +750,6 @@ static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 l return; } - _BUG_ON(!irqs_disabled()); - RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf); #if USE_32BIT if (len >= 4) { @@ -923,10 +911,6 @@ static void __attribute__((__unused__)) isp1362_show_regs(struct isp1362_hcd *is static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len) { - _BUG_ON(offset & 1); - _BUG_ON(offset >= ISP1362_BUF_SIZE); - _BUG_ON(len > ISP1362_BUF_SIZE); - _BUG_ON(offset + len > ISP1362_BUF_SIZE); len = (len + 1) & ~1; isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE); @@ -936,8 +920,6 @@ static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) { - _BUG_ON(offset & 1); - isp1362_write_diraddr(isp1362_hcd, offset, len); DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %p\n", @@ -956,8 +938,6 @@ static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) { - _BUG_ON(offset & 1); - isp1362_write_diraddr(isp1362_hcd, offset, len); DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %p\n", -- cgit v1.2.3-58-ga151 From 2809508d3b63c9fedf8daf23d08dd61f40a761f4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:32:59 -0700 Subject: USB: isp1362: remove unused _WARN_ON() calls Like _BUG_ON(), _WARN_ON() wasn't ever being used, so just delete it, as obviously things are working properly now (if not, we have bigger problems...) Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index 4ef746dcef85..e0f6f922052d 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -91,8 +91,6 @@ typedef const unsigned int isp1362_reg_t; #define ISP1362_REG_NO(r) ((r) & REG_NO_MASK) -#define _WARN_ON(x) WARN_ON(x) - #define ISP1362_REG(name, addr, width, rw) \ static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw)) @@ -101,7 +99,6 @@ static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw)) #else typedef const unsigned char isp1362_reg_t; #define ISP1362_REG_NO(r) (r) -#define _WARN_ON(x) do {} while (0) #define ISP1362_REG(name, addr, width, rw) \ static isp1362_reg_t ISP1362_REG_##name = addr @@ -926,14 +923,11 @@ static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 __func__, len, offset, buf); isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); - _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA); isp1362_read_fifo(isp1362_hcd, buf, len); - _WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); - _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); } static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) @@ -944,14 +938,11 @@ static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 __func__, len, offset, buf); isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); - _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET); isp1362_write_fifo(isp1362_hcd, buf, len); - _WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); - _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); } static void __attribute__((unused)) dump_data(char *buf, int len) -- cgit v1.2.3-58-ga151 From 374f4bfdf5b6a37a641c13ef22e0bbb42e0b3ac7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:33:00 -0700 Subject: USB: isp1362: remove _DBG() usage If you want a debug call, just make it, so move to using the already-there DBG() call. No need to make things more complex than they really need to be. Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 30 +++++++++++++++--------------- drivers/usb/host/isp1362.h | 6 ------ 2 files changed, 15 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index c747ba35f731..428d184ceb33 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -1573,12 +1573,12 @@ static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, DBG(0, "ClearHubFeature: "); switch (wValue) { case C_HUB_OVER_CURRENT: - _DBG(0, "C_HUB_OVER_CURRENT\n"); + DBG(0, "C_HUB_OVER_CURRENT\n"); spin_lock_irqsave(&isp1362_hcd->lock, flags); isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_OCIC); spin_unlock_irqrestore(&isp1362_hcd->lock, flags); case C_HUB_LOCAL_POWER: - _DBG(0, "C_HUB_LOCAL_POWER\n"); + DBG(0, "C_HUB_LOCAL_POWER\n"); break; default: goto error; @@ -1589,7 +1589,7 @@ static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: - _DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n"); + DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n"); break; default: goto error; @@ -1620,36 +1620,36 @@ static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, switch (wValue) { case USB_PORT_FEAT_ENABLE: - _DBG(0, "USB_PORT_FEAT_ENABLE\n"); + DBG(0, "USB_PORT_FEAT_ENABLE\n"); tmp = RH_PS_CCS; break; case USB_PORT_FEAT_C_ENABLE: - _DBG(0, "USB_PORT_FEAT_C_ENABLE\n"); + DBG(0, "USB_PORT_FEAT_C_ENABLE\n"); tmp = RH_PS_PESC; break; case USB_PORT_FEAT_SUSPEND: - _DBG(0, "USB_PORT_FEAT_SUSPEND\n"); + DBG(0, "USB_PORT_FEAT_SUSPEND\n"); tmp = RH_PS_POCI; break; case USB_PORT_FEAT_C_SUSPEND: - _DBG(0, "USB_PORT_FEAT_C_SUSPEND\n"); + DBG(0, "USB_PORT_FEAT_C_SUSPEND\n"); tmp = RH_PS_PSSC; break; case USB_PORT_FEAT_POWER: - _DBG(0, "USB_PORT_FEAT_POWER\n"); + DBG(0, "USB_PORT_FEAT_POWER\n"); tmp = RH_PS_LSDA; break; case USB_PORT_FEAT_C_CONNECTION: - _DBG(0, "USB_PORT_FEAT_C_CONNECTION\n"); + DBG(0, "USB_PORT_FEAT_C_CONNECTION\n"); tmp = RH_PS_CSC; break; case USB_PORT_FEAT_C_OVER_CURRENT: - _DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n"); + DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n"); tmp = RH_PS_OCIC; break; case USB_PORT_FEAT_C_RESET: - _DBG(0, "USB_PORT_FEAT_C_RESET\n"); + DBG(0, "USB_PORT_FEAT_C_RESET\n"); tmp = RH_PS_PRSC; break; default: @@ -1669,7 +1669,7 @@ static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, wIndex--; switch (wValue) { case USB_PORT_FEAT_SUSPEND: - _DBG(0, "USB_PORT_FEAT_SUSPEND\n"); + DBG(0, "USB_PORT_FEAT_SUSPEND\n"); spin_lock_irqsave(&isp1362_hcd->lock, flags); isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PSS); isp1362_hcd->rhport[wIndex] = @@ -1677,7 +1677,7 @@ static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_unlock_irqrestore(&isp1362_hcd->lock, flags); break; case USB_PORT_FEAT_POWER: - _DBG(0, "USB_PORT_FEAT_POWER\n"); + DBG(0, "USB_PORT_FEAT_POWER\n"); spin_lock_irqsave(&isp1362_hcd->lock, flags); isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PPS); isp1362_hcd->rhport[wIndex] = @@ -1685,7 +1685,7 @@ static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_unlock_irqrestore(&isp1362_hcd->lock, flags); break; case USB_PORT_FEAT_RESET: - _DBG(0, "USB_PORT_FEAT_RESET\n"); + DBG(0, "USB_PORT_FEAT_RESET\n"); spin_lock_irqsave(&isp1362_hcd->lock, flags); t1 = jiffies + msecs_to_jiffies(USB_RESET_WIDTH); @@ -1719,7 +1719,7 @@ static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, default: error: /* "protocol stall" on error */ - _DBG(0, "PROTOCOL STALL\n"); + DBG(0, "PROTOCOL STALL\n"); retval = -EPIPE; } diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index e0f6f922052d..d36a90868900 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -588,14 +588,8 @@ static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd if (dbg_level > level) \ pr_debug(fmt); \ } while (0) -#define _DBG(level, fmt...) \ - do { \ - if (dbg_level > level) \ - printk(fmt); \ - } while (0) #else #define DBG(fmt...) do {} while (0) -#define _DBG DBG #endif #ifdef VERBOSE -- cgit v1.2.3-58-ga151 From 641c86cd1cc6cb42233500a20e85ae5027826cfa Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:33:01 -0700 Subject: USB: isp1362: remove CONFIG_USB_DEBUG dependency Now that the debugging macros are cleaned up, just rely on the dynamic debug code in the kernel to do the debug messages for the driver. This lets debugging be enabled without having to rebuild the driver, an important thing for users that can not do it. Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 6 +----- drivers/usb/host/isp1362.h | 16 ++++------------ 2 files changed, 5 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 428d184ceb33..fd76256c7e74 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -37,11 +37,7 @@ * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz. */ -#ifdef CONFIG_USB_DEBUG -# define ISP1362_DEBUG -#else -# undef ISP1362_DEBUG -#endif +#undef ISP1362_DEBUG /* * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index d36a90868900..7b34699f5a65 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -76,14 +76,14 @@ static inline void delayed_insw(unsigned int addr, void *buf, int len) #define ISP1362_REG_WRITE_OFFSET 0x80 -#ifdef ISP1362_DEBUG -typedef const unsigned int isp1362_reg_t; - #define REG_WIDTH_16 0x000 #define REG_WIDTH_32 0x100 #define REG_WIDTH_MASK 0x100 #define REG_NO_MASK 0x0ff +#ifdef ISP1362_DEBUG +typedef const unsigned int isp1362_reg_t; + #define REG_ACCESS_R 0x200 #define REG_ACCESS_W 0x400 #define REG_ACCESS_RW 0x600 @@ -582,15 +582,11 @@ static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd * ISP1362 HW Interface */ -#ifdef ISP1362_DEBUG #define DBG(level, fmt...) \ do { \ if (dbg_level > level) \ pr_debug(fmt); \ } while (0) -#else -#define DBG(fmt...) do {} while (0) -#endif #ifdef VERBOSE # define VDBG(fmt...) DBG(3, fmt) @@ -833,7 +829,6 @@ static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 l isp1362_write_reg32(d, r, __v & ~m); \ } -#ifdef ISP1362_DEBUG #define isp1362_show_reg(d, r) { \ if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \ DBG(0, "%-12s[%02x]: %08x\n", #r, \ @@ -842,9 +837,6 @@ static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 l DBG(0, "%-12s[%02x]: %04x\n", #r, \ ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \ } -#else -#define isp1362_show_reg(d, r) do {} while (0) -#endif static void __attribute__((__unused__)) isp1362_show_regs(struct isp1362_hcd *isp1362_hcd) { @@ -967,7 +959,7 @@ static void __attribute__((unused)) dump_data(char *buf, int len) } } -#if defined(ISP1362_DEBUG) && defined(PTD_TRACE) +#if defined(PTD_TRACE) static void dump_ptd(struct ptd *ptd) { -- cgit v1.2.3-58-ga151 From 1d896ceae46d61412a575a4fc269f5eb4b0397d6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:33:02 -0700 Subject: USB: isp116x: remove dependency on CONFIG_USB_DEBUG Move all debugging messages in the driver to use the dynamic debug subsystem, and not rely on CONFIG_USB_DEBUG to turn them on or off. This lets debugging be enabled without having to rebuild the driver, an important thing for users that can not do it. It also removes the pointless IRQ_TEST() macro, as that was totally useless and obviously never used. Cc: Olav Kongas Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp116x.h | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h index 9a2c400e6090..dd34b7a33965 100644 --- a/drivers/usb/host/isp116x.h +++ b/drivers/usb/host/isp116x.h @@ -325,11 +325,7 @@ struct isp116x_ep { /*-------------------------------------------------------------------------*/ -#ifdef DEBUG -#define DBG(stuff...) printk(KERN_DEBUG "116x: " stuff) -#else -#define DBG(stuff...) do{}while(0) -#endif +#define DBG(stuff...) pr_debug("116x: " stuff) #ifdef VERBOSE # define VDBG DBG @@ -358,15 +354,8 @@ struct isp116x_ep { #define isp116x_check_platform_delay(h) 0 #endif -#if defined(DEBUG) -#define IRQ_TEST() BUG_ON(!irqs_disabled()) -#else -#define IRQ_TEST() do{}while(0) -#endif - static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg) { - IRQ_TEST(); writew(reg & 0xff, isp116x->addr_reg); isp116x_delay(isp116x, 300); } -- cgit v1.2.3-58-ga151 From e45282829a814a7f1e857427f218b21aef012e52 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Jun 2013 11:33:03 -0700 Subject: USB: sl811: remove CONFIG_USB_DEBUG dependency This removes the dependency of the driver on CONFIG_USB_DEBUG and moves it to us the dynamic debug subsystem instead. Bonus is the fact that we can now properly determine the exact hardware that is spitting out the messages. This lets debugging be enabled without having to rebuild the driver, an important thing for users that can not do it. Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 69 ++++++++++++++++++++++++++------------------ drivers/usb/host/sl811.h | 19 +----------- 2 files changed, 42 insertions(+), 46 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index b2ec7fe758dd..c11c5d8f94e0 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -100,7 +100,8 @@ static void port_power(struct sl811 *sl811, int is_on) if (sl811->board && sl811->board->port_power) { /* switch VBUS, at 500mA unless hub power budget gets set */ - DBG("power %s\n", is_on ? "on" : "off"); + dev_dbg(hcd->self.controller, "power %s\n", + is_on ? "on" : "off"); sl811->board->port_power(hcd->self.controller, is_on); } @@ -282,7 +283,7 @@ static inline void sofirq_on(struct sl811 *sl811) { if (sl811->irq_enable & SL11H_INTMASK_SOFINTR) return; - VDBG("sof irq on\n"); + dev_dbg(sl811_to_hcd(sl811)->self.controller, "sof irq on\n"); sl811->irq_enable |= SL11H_INTMASK_SOFINTR; } @@ -290,7 +291,7 @@ static inline void sofirq_off(struct sl811 *sl811) { if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR)) return; - VDBG("sof irq off\n"); + dev_dbg(sl811_to_hcd(sl811)->self.controller, "sof irq off\n"); sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR; } @@ -338,7 +339,8 @@ static struct sl811h_ep *start(struct sl811 *sl811, u8 bank) } if (unlikely(list_empty(&ep->hep->urb_list))) { - DBG("empty %p queue?\n", ep); + dev_dbg(sl811_to_hcd(sl811)->self.controller, + "empty %p queue?\n", ep); return NULL; } @@ -391,7 +393,8 @@ static struct sl811h_ep *start(struct sl811 *sl811, u8 bank) status_packet(sl811, ep, urb, bank, control); break; default: - DBG("bad ep%p pid %02x\n", ep, ep->nextpid); + dev_dbg(sl811_to_hcd(sl811)->self.controller, + "bad ep%p pid %02x\n", ep, ep->nextpid); ep = NULL; } return ep; @@ -447,7 +450,8 @@ static void finish_request( } /* periodic deschedule */ - DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + dev_dbg(sl811_to_hcd(sl811)->self.controller, + "deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { struct sl811h_ep *temp; struct sl811h_ep **prev = &sl811->periodic[i]; @@ -593,7 +597,8 @@ static inline u8 checkdone(struct sl811 *sl811) ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)); if (ctl & SL11H_HCTLMASK_ARM) sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); - DBG("%s DONE_A: ctrl %02x sts %02x\n", + dev_dbg(sl811_to_hcd(sl811)->self.controller, + "%s DONE_A: ctrl %02x sts %02x\n", (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", ctl, sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); @@ -604,7 +609,8 @@ static inline u8 checkdone(struct sl811 *sl811) ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)); if (ctl & SL11H_HCTLMASK_ARM) sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); - DBG("%s DONE_B: ctrl %02x sts %02x\n", + dev_dbg(sl811_to_hcd(sl811)->self.controller, + "%s DONE_B: ctrl %02x sts %02x\n", (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", ctl, sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); @@ -665,7 +671,7 @@ retry: * this one has nothing scheduled. */ if (sl811->next_periodic) { - // ERR("overrun to slot %d\n", index); + // dev_err(hcd->self.controller, "overrun to slot %d\n", index); sl811->stat_overrun++; } if (sl811->periodic[index]) @@ -723,7 +729,7 @@ retry: } else if (irqstat & SL11H_INTMASK_RD) { if (sl811->port1 & USB_PORT_STAT_SUSPEND) { - DBG("wakeup\n"); + dev_dbg(hcd->self.controller, "wakeup\n"); sl811->port1 |= USB_PORT_STAT_C_SUSPEND << 16; sl811->stat_wake++; } else @@ -852,8 +858,9 @@ static int sl811h_urb_enqueue( if (ep->maxpacket > H_MAXPACKET) { /* iso packets up to 240 bytes could work... */ - DBG("dev %d ep%d maxpacket %d\n", - udev->devnum, epnum, ep->maxpacket); + dev_dbg(hcd->self.controller, + "dev %d ep%d maxpacket %d\n", udev->devnum, + epnum, ep->maxpacket); retval = -EINVAL; kfree(ep); goto fail; @@ -917,7 +924,8 @@ static int sl811h_urb_enqueue( * to share the faster parts of the tree without needing * dummy/placeholder nodes */ - DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + dev_dbg(hcd->self.controller, "schedule qh%d/%p branch %d\n", + ep->period, ep, ep->branch); for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { struct sl811h_ep **prev = &sl811->periodic[i]; struct sl811h_ep *here = *prev; @@ -976,7 +984,8 @@ static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } else if (sl811->active_a == ep) { if (time_before_eq(sl811->jiffies_a, jiffies)) { /* happens a lot with lowspeed?? */ - DBG("giveup on DONE_A: ctrl %02x sts %02x\n", + dev_dbg(hcd->self.controller, + "giveup on DONE_A: ctrl %02x sts %02x\n", sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)), sl811_read(sl811, @@ -990,7 +999,8 @@ static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } else if (sl811->active_b == ep) { if (time_before_eq(sl811->jiffies_a, jiffies)) { /* happens a lot with lowspeed?? */ - DBG("giveup on DONE_B: ctrl %02x sts %02x\n", + dev_dbg(hcd->self.controller, + "giveup on DONE_B: ctrl %02x sts %02x\n", sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)), sl811_read(sl811, @@ -1008,7 +1018,8 @@ static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) if (urb) finish_request(sl811, ep, urb, 0); else - VDBG("dequeue, urb %p active %s; wait4irq\n", urb, + dev_dbg(sl811_to_hcd(sl811)->self.controller, + "dequeue, urb %p active %s; wait4irq\n", urb, (sl811->active_a == ep) ? "A" : "B"); } else retval = -EINVAL; @@ -1029,7 +1040,7 @@ sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) if (!list_empty(&hep->urb_list)) msleep(3); if (!list_empty(&hep->urb_list)) - WARNING("ep %p not empty?\n", ep); + dev_warn(hcd->self.controller, "ep %p not empty?\n", ep); kfree(ep); hep->hcpriv = NULL; @@ -1132,7 +1143,7 @@ sl811h_timer(unsigned long _sl811) switch (signaling) { case SL11H_CTL1MASK_SE0: - DBG("end reset\n"); + dev_dbg(sl811_to_hcd(sl811)->self.controller, "end reset\n"); sl811->port1 = (USB_PORT_STAT_C_RESET << 16) | USB_PORT_STAT_POWER; sl811->ctrl1 = 0; @@ -1141,11 +1152,12 @@ sl811h_timer(unsigned long _sl811) irqstat &= ~SL11H_INTMASK_RD; break; case SL11H_CTL1MASK_K: - DBG("end resume\n"); + dev_dbg(sl811_to_hcd(sl811)->self.controller, "end resume\n"); sl811->port1 &= ~USB_PORT_STAT_SUSPEND; break; default: - DBG("odd timer signaling: %02x\n", signaling); + dev_dbg(sl811_to_hcd(sl811)->self.controller, + "odd timer signaling: %02x\n", signaling); break; } sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); @@ -1243,7 +1255,7 @@ sl811h_hub_control( break; /* 20 msec of resume/K signaling, other irqs blocked */ - DBG("start resume...\n"); + dev_dbg(hcd->self.controller, "start resume...\n"); sl811->irq_enable = 0; sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); @@ -1281,7 +1293,8 @@ sl811h_hub_control( #ifndef VERBOSE if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ #endif - DBG("GetPortStatus %08x\n", sl811->port1); + dev_dbg(hcd->self.controller, "GetPortStatus %08x\n", + sl811->port1); break; case SetPortFeature: if (wIndex != 1 || wLength != 0) @@ -1293,7 +1306,7 @@ sl811h_hub_control( if (!(sl811->port1 & USB_PORT_STAT_ENABLE)) goto error; - DBG("suspend...\n"); + dev_dbg(hcd->self.controller,"suspend...\n"); sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA; sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); break; @@ -1338,7 +1351,7 @@ static int sl811h_bus_suspend(struct usb_hcd *hcd) { // SOFs off - DBG("%s\n", __func__); + dev_dbg(hcd->self.controller, "%s\n", __func__); return 0; } @@ -1346,7 +1359,7 @@ static int sl811h_bus_resume(struct usb_hcd *hcd) { // SOFs on - DBG("%s\n", __func__); + dev_dbg(hcd->self.controller, "%s\n", __func__); return 0; } @@ -1648,7 +1661,7 @@ sl811h_probe(struct platform_device *dev) /* refuse to confuse usbcore */ if (dev->dev.dma_mask) { - DBG("no we won't dma\n"); + dev_dbg(&dev->dev, "no we won't dma\n"); return -EINVAL; } @@ -1716,7 +1729,7 @@ sl811h_probe(struct platform_device *dev) break; default: /* reject case 0, SL11S is less functional */ - DBG("chiprev %02x\n", tmp); + dev_dbg(&dev->dev, "chiprev %02x\n", tmp); retval = -ENXIO; goto err6; } @@ -1747,7 +1760,7 @@ sl811h_probe(struct platform_device *dev) if (!ioaddr) iounmap(addr_reg); err2: - DBG("init error, %d\n", retval); + dev_dbg(&dev->dev, "init error, %d\n", retval); return retval; } diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h index b6b8c1f233dd..7ac5c5ee4a3e 100644 --- a/drivers/usb/host/sl811.h +++ b/drivers/usb/host/sl811.h @@ -242,25 +242,8 @@ sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count) /*-------------------------------------------------------------------------*/ -#ifdef DEBUG -#define DBG(stuff...) printk(KERN_DEBUG "sl811: " stuff) -#else -#define DBG(stuff...) do{}while(0) -#endif - -#ifdef VERBOSE -# define VDBG DBG -#else -# define VDBG(stuff...) do{}while(0) -#endif - #ifdef PACKET_TRACE -# define PACKET VDBG +# define PACKET pr_debug("sl811: "stuff) #else # define PACKET(stuff...) do{}while(0) #endif - -#define ERR(stuff...) printk(KERN_ERR "sl811: " stuff) -#define WARNING(stuff...) printk(KERN_WARNING "sl811: " stuff) -#define INFO(stuff...) printk(KERN_INFO "sl811: " stuff) - -- cgit v1.2.3-58-ga151 From 2e6a9e8428d2c391b94d3bcd01c3a3319a1f90b3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Jul 2013 12:22:05 -0700 Subject: USB: remove unneeded idr.h include None of these USB files need idr.h, so don't include it. Cc: Alexander Shishkin Reviewed-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/endpoint.c | 1 - drivers/usb/musb/musb_core.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 68cc6532e749..f13a289a0743 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include "usb.h" diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 29a24ced6748..9e59710efee7 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -99,7 +99,6 @@ #include #include #include -#include #include #include "musb_core.h" -- cgit v1.2.3-58-ga151 From 0511b36399434d49d3a6b58cf49be90d96e66848 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Jul 2013 12:22:06 -0700 Subject: USB: sl811: move debug files from proc to debugfs Drivers should not be putting debug files in /proc/ that is what debugfs is for, so move the sl811 driver's debug file to debugfs. Reviewed-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 38 +++++++++++--------------------------- drivers/usb/host/sl811.h | 2 +- 2 files changed, 12 insertions(+), 28 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index c11c5d8f94e0..d8938630e092 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #include @@ -63,11 +65,6 @@ MODULE_ALIAS("platform:sl811-hcd"); #define DRIVER_VERSION "19 May 2005" - -#ifndef DEBUG -# define STUB_DEBUG_FILE -#endif - /* for now, use only one transfer register bank */ #undef USE_B @@ -1373,16 +1370,6 @@ sl811h_bus_resume(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ -#ifdef STUB_DEBUG_FILE - -static inline void create_debug_file(struct sl811 *sl811) { } -static inline void remove_debug_file(struct sl811 *sl811) { } - -#else - -#include -#include - static void dump_irq(struct seq_file *s, char *label, u8 mask) { seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask, @@ -1394,7 +1381,7 @@ static void dump_irq(struct seq_file *s, char *label, u8 mask) (mask & SL11H_INTMASK_DP) ? " dp" : ""); } -static int proc_sl811h_show(struct seq_file *s, void *unused) +static int sl811h_show(struct seq_file *s, void *unused) { struct sl811 *sl811 = s->private; struct sl811h_ep *ep; @@ -1505,34 +1492,31 @@ static int proc_sl811h_show(struct seq_file *s, void *unused) return 0; } -static int proc_sl811h_open(struct inode *inode, struct file *file) +static int sl811h_open(struct inode *inode, struct file *file) { - return single_open(file, proc_sl811h_show, PDE_DATA(inode)); + return single_open(file, sl811h_show, inode->i_private); } -static const struct file_operations proc_ops = { - .open = proc_sl811h_open, +static const struct file_operations debug_ops = { + .open = sl811h_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; /* expect just one sl811 per system */ -static const char proc_filename[] = "driver/sl811h"; - static void create_debug_file(struct sl811 *sl811) { - sl811->pde = proc_create_data(proc_filename, 0, NULL, &proc_ops, sl811); + sl811->debug_file = debugfs_create_file("sl811h", S_IRUGO, + usb_debug_root, sl811, + &debug_ops); } static void remove_debug_file(struct sl811 *sl811) { - if (sl811->pde) - remove_proc_entry(proc_filename, NULL); + debugfs_remove(sl811->debug_file); } -#endif - /*-------------------------------------------------------------------------*/ static void diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h index 7ac5c5ee4a3e..1e23ef49bec1 100644 --- a/drivers/usb/host/sl811.h +++ b/drivers/usb/host/sl811.h @@ -122,7 +122,7 @@ struct sl811 { void __iomem *addr_reg; void __iomem *data_reg; struct sl811_platform_data *board; - struct proc_dir_entry *pde; + struct dentry *debug_file; unsigned long stat_insrmv; unsigned long stat_wake; -- cgit v1.2.3-58-ga151 From a6363463e8f5c7996aff6fa1285ff6c7cc3d3f6d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Jul 2013 12:22:07 -0700 Subject: USB: isp1362: move debug files from proc to debugfs Drivers should not be putting debug files in /proc/ that is what debugfs is for, so move the isp1362 driver's debug file to debugfs. Reviewed-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 45 +++++++++++------------------------------- drivers/usb/host/isp1362.h | 2 +- 2 files changed, 12 insertions(+), 35 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index fd76256c7e74..c15463b898e2 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -78,6 +78,8 @@ #include #include #include +#include +#include #include #include @@ -88,7 +90,6 @@ static int dbg_level; module_param(dbg_level, int, 0644); #else module_param(dbg_level, int, 0); -#define STUB_DEBUG_FILE #endif #include "../core/usb.h" @@ -1907,20 +1908,6 @@ static int isp1362_bus_resume(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ -#ifdef STUB_DEBUG_FILE - -static inline void create_debug_file(struct isp1362_hcd *isp1362_hcd) -{ -} -static inline void remove_debug_file(struct isp1362_hcd *isp1362_hcd) -{ -} - -#else - -#include -#include - static void dump_irq(struct seq_file *s, char *label, u16 mask) { seq_printf(s, "%-15s %04x%s%s%s%s%s%s\n", label, mask, @@ -2063,7 +2050,7 @@ static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd) isp1362_read_reg16(isp1362_hcd, HCATLDTCTO)); } -static int proc_isp1362_show(struct seq_file *s, void *unused) +static int isp1362_show(struct seq_file *s, void *unused) { struct isp1362_hcd *isp1362_hcd = s->private; struct isp1362_ep *ep; @@ -2167,41 +2154,31 @@ static int proc_isp1362_show(struct seq_file *s, void *unused) return 0; } -static int proc_isp1362_open(struct inode *inode, struct file *file) +static int isp1362_open(struct inode *inode, struct file *file) { - return single_open(file, proc_isp1362_show, PDE_DATA(inode)); + return single_open(file, isp1362_show, inode); } -static const struct file_operations proc_ops = { - .open = proc_isp1362_open, +static const struct file_operations debug_ops = { + .open = isp1362_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; /* expect just one isp1362_hcd per system */ -static const char proc_filename[] = "driver/isp1362"; - static void create_debug_file(struct isp1362_hcd *isp1362_hcd) { - struct proc_dir_entry *pde; - - pde = proc_create_data(proc_filename, 0, NULL, &proc_ops, isp1362_hcd); - if (pde == NULL) { - pr_warning("%s: Failed to create debug file '%s'\n", __func__, proc_filename); - return; - } - isp1362_hcd->pde = pde; + isp1362_hcd->debug_file = debugfs_create_file("isp1362", S_IRUGO, + usb_debug_root, + isp1362_hcd, &debug_ops); } static void remove_debug_file(struct isp1362_hcd *isp1362_hcd) { - if (isp1362_hcd->pde) - remove_proc_entry(proc_filename, NULL); + debugfs_remove(isp1362_hcd->debug_file); } -#endif - /*-------------------------------------------------------------------------*/ static void __isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd) diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index 7b34699f5a65..3b0b4847c3a9 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -480,7 +480,7 @@ struct isp1362_hcd { struct isp1362_platform_data *board; - struct proc_dir_entry *pde; + struct dentry *debug_file; unsigned long stat1, stat2, stat4, stat8, stat16; /* HC registers */ -- cgit v1.2.3-58-ga151 From 10e232c597ac757e7f8600649f7e872e86de190f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 28 Jun 2013 09:38:12 +0800 Subject: USB: check sg buffer size in usb_submit_urb USB spec stats that short packet can only appear at the end of transfer. Because lost of HC(EHCI/UHCI/OHCI/...) can't build a full packet from discontinuous buffers, we introduce the limit in usb_submit_urb() to avoid such kind of bad sg buffers coming from driver. The limit might be a bit strict: - platform has iommu to do sg list mapping - some host controllers may support to build full packet from discontinuous buffers. But considered that most of HCs don't support that, and driver need work well or keep consistent on different HCs and ARCHs, we have to introduce the limit. Currently, only usbtest is reported to pass such sg buffers to HC, and other users(mass storage, usbfs) don't have the problem. We don't check it on USB wireless device, because: - wireless devices can't be attached to common USB bus(EHCI/UHCI/OHCI/...) - the max packet size of endpoint may be odd, and often can't devide 4KB which is a typical usage in usb mass storage application Reported-by: Konstantin Filatov Reported-by: Denis V. Lunev Cc: Felipe Balbi Acked-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 8 ++++++++ include/linux/usb.h | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 16927fa88fbd..e75115a04a2e 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -7,6 +7,7 @@ #include #include #include +#include #define to_urb(d) container_of(d, struct urb, kref) @@ -413,6 +414,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) urb->iso_frame_desc[n].status = -EXDEV; urb->iso_frame_desc[n].actual_length = 0; } + } else if (dev->speed != USB_SPEED_WIRELESS && urb->num_sgs) { + struct scatterlist *sg; + int i; + + for_each_sg(urb->sg, sg, urb->num_sgs - 1, i) + if (sg->length % max) + return -EINVAL; } /* the I/O buffer must be mapped/unmapped, except when length=0 */ diff --git a/include/linux/usb.h b/include/linux/usb.h index a232b7ece1f6..e99b2a114af4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1258,7 +1258,9 @@ typedef void (*usb_complete_t)(struct urb *); * the device driver is saying that it provided this DMA address, * which the host controller driver should use in preference to the * transfer_buffer. - * @sg: scatter gather buffer list + * @sg: scatter gather buffer list, the buffer size of each element in + * the list (except the last) must be divisible by the endpoint's + * max packet size * @num_mapped_sgs: (internal) number of mapped sg entries * @num_sgs: number of entries in the sg list * @transfer_buffer_length: How big is transfer_buffer. The transfer may -- cgit v1.2.3-58-ga151 From 1dd3d123239179fad5de5dc00a6e0014a1918fde Mon Sep 17 00:00:00 2001 From: Yuan-Hsin Chen Date: Wed, 19 Jun 2013 19:53:04 +0000 Subject: usb: host: Faraday fotg210-hcd driver FOTG210 is an OTG controller which can be configured as an USB2.0 host. FOTG210 host is an ehci-like controller with some differences. First, register layout of FOTG210 is incompatible with EHCI. Furthermore, FOTG210 is lack of siTDs which means iTDs are used for both HS and FS ISO transfer. Signed-off-by: Yuan-Hsin Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 + drivers/usb/host/Kconfig | 12 + drivers/usb/host/Makefile | 1 + drivers/usb/host/fotg210-hcd.c | 6049 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/fotg210.h | 750 +++++ 5 files changed, 6813 insertions(+) create mode 100644 drivers/usb/host/fotg210-hcd.c create mode 100644 drivers/usb/host/fotg210.h (limited to 'drivers/usb') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 238c5d47cadb..5460abf045de 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ obj-$(CONFIG_USB_FUSBH200_HCD) += host/ +obj-$(CONFIG_USB_FOTG210_HCD) += host/ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 4263d011392c..cf521d6551dd 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -354,6 +354,18 @@ config USB_FUSBH200_HCD To compile this driver as a module, choose M here: the module will be called fusbh200-hcd. +config USB_FOTG210_HCD + tristate "FOTG210 HCD support" + depends on USB + default N + ---help--- + Faraday FOTG210 is an OTG controller which can be configured as + an USB2.0 host. It is designed to meet USB2.0 EHCI specification + with minor modification. + + To compile this driver as a module, choose M here: the + module will be called fotg210-hcd. + config USB_OHCI_HCD tristate "OHCI HCD (USB 1.1) support" select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index bea71127b15f..829a3397882a 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o obj-$(CONFIG_USB_FUSBH200_HCD) += fusbh200-hcd.o +obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c new file mode 100644 index 000000000000..fce13bcc4a3e --- /dev/null +++ b/drivers/usb/host/fotg210-hcd.c @@ -0,0 +1,6049 @@ +/* + * Faraday FOTG210 EHCI-like driver + * + * Copyright (c) 2013 Faraday Technology Corporation + * + * Author: Yuan-Hsin Chen + * Feng-Hsin Chiang + * Po-Yu Chuang + * + * Most of code borrowed from the Linux-3.7 EHCI driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/*-------------------------------------------------------------------------*/ +#define DRIVER_AUTHOR "Yuan-Hsin Chen" +#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" + +static const char hcd_name[] = "fotg210_hcd"; + +#undef VERBOSE_DEBUG +#undef FOTG210_URB_TRACE + +#ifdef DEBUG +#define FOTG210_STATS +#endif + +/* magic numbers that can affect system performance */ +#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define FOTG210_TUNE_RL_TT 0 +#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define FOTG210_TUNE_MULT_TT 1 +/* + * Some drivers think it's safe to schedule isochronous transfers more than + * 256 ms into the future (partly as a result of an old bug in the scheduling + * code). In an attempt to avoid trouble, we will use a minimum scheduling + * length of 512 frames instead of 256. + */ +#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ + +/* Initial IRQ latency: faster than hw default */ +static int log2_irq_thresh; /* 0 to 6 */ +module_param(log2_irq_thresh, int, S_IRUGO); +MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); + +/* initial park setting: slower than hw default */ +static unsigned park; +module_param(park, uint, S_IRUGO); +MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); + +/* for link power management(LPM) feature */ +static unsigned int hird; +module_param(hird, int, S_IRUGO); +MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); + +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) + +#include "fotg210.h" + +/*-------------------------------------------------------------------------*/ + +#define fotg210_dbg(fotg210, fmt, args...) \ + dev_dbg(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) +#define fotg210_err(fotg210, fmt, args...) \ + dev_err(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) +#define fotg210_info(fotg210, fmt, args...) \ + dev_info(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) +#define fotg210_warn(fotg210, fmt, args...) \ + dev_warn(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) + +#ifdef VERBOSE_DEBUG +# define fotg210_vdbg fotg210_dbg +#else + static inline void fotg210_vdbg(struct fotg210_hcd *fotg210, ...) {} +#endif + +#ifdef DEBUG + +/* check the values in the HCSPARAMS register + * (host controller _Structural_ parameters) + * see EHCI spec, Table 2-4 for each value + */ +static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) +{ + u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); + + fotg210_dbg(fotg210, + "%s hcs_params 0x%x ports=%d\n", + label, params, + HCS_N_PORTS(params) + ); +} +#else + +static inline void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) {} + +#endif + +#ifdef DEBUG + +/* check the values in the HCCPARAMS register + * (host controller _Capability_ parameters) + * see EHCI Spec, Table 2-5 for each value + * */ +static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) +{ + u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); + + fotg210_dbg(fotg210, + "%s hcc_params %04x uframes %s%s\n", + label, + params, + HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", + HCC_CANPARK(params) ? " park" : ""); +} +#else + +static inline void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) {} + +#endif + +#ifdef DEBUG + +static void __maybe_unused +dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) +{ + fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, + hc32_to_cpup(fotg210, &qtd->hw_next), + hc32_to_cpup(fotg210, &qtd->hw_alt_next), + hc32_to_cpup(fotg210, &qtd->hw_token), + hc32_to_cpup(fotg210, &qtd->hw_buf[0])); + if (qtd->hw_buf[1]) + fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", + hc32_to_cpup(fotg210, &qtd->hw_buf[1]), + hc32_to_cpup(fotg210, &qtd->hw_buf[2]), + hc32_to_cpup(fotg210, &qtd->hw_buf[3]), + hc32_to_cpup(fotg210, &qtd->hw_buf[4])); +} + +static void __maybe_unused +dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + struct fotg210_qh_hw *hw = qh->hw; + + fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, + qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current); + dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); +} + +static void __maybe_unused +dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) +{ + fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", + label, itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), + itd->urb); + fotg210_dbg(fotg210, + " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", + hc32_to_cpu(fotg210, itd->hw_transaction[0]), + hc32_to_cpu(fotg210, itd->hw_transaction[1]), + hc32_to_cpu(fotg210, itd->hw_transaction[2]), + hc32_to_cpu(fotg210, itd->hw_transaction[3]), + hc32_to_cpu(fotg210, itd->hw_transaction[4]), + hc32_to_cpu(fotg210, itd->hw_transaction[5]), + hc32_to_cpu(fotg210, itd->hw_transaction[6]), + hc32_to_cpu(fotg210, itd->hw_transaction[7])); + fotg210_dbg(fotg210, + " buf: %08x %08x %08x %08x %08x %08x %08x\n", + hc32_to_cpu(fotg210, itd->hw_bufp[0]), + hc32_to_cpu(fotg210, itd->hw_bufp[1]), + hc32_to_cpu(fotg210, itd->hw_bufp[2]), + hc32_to_cpu(fotg210, itd->hw_bufp[3]), + hc32_to_cpu(fotg210, itd->hw_bufp[4]), + hc32_to_cpu(fotg210, itd->hw_bufp[5]), + hc32_to_cpu(fotg210, itd->hw_bufp[6])); + fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", + itd->index[0], itd->index[1], itd->index[2], + itd->index[3], itd->index[4], itd->index[5], + itd->index[6], itd->index[7]); +} + +static int __maybe_unused +dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +{ + return scnprintf(buf, len, + "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +} + +static int __maybe_unused +dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +{ + return scnprintf(buf, len, + "%s%sintrenable %02x%s%s%s%s%s%s", + label, label[0] ? " " : "", enable, + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", + (enable & STS_PCD) ? " PCD" : "", + (enable & STS_ERR) ? " ERR" : "", + (enable & STS_INT) ? " INT" : "" + ); +} + +static const char *const fls_strings[] = { "1024", "512", "256", "??" }; + +static int +dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) +{ + return scnprintf(buf, len, + "%s%scommand %07x %s=%d ithresh=%d%s%s%s " + "period=%s%s %s", + label, label[0] ? " " : "", command, + (command & CMD_PARK) ? " park" : "(park)", + CMD_PARK_CNT(command), + (command >> 16) & 0x3f, + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + fls_strings[(command >> 2) & 0x3], + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +} + +static int +dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) +{ + char *sig; + + /* signaling state */ + switch (status & (3 << 10)) { + case 0 << 10: + sig = "se0"; + break; + case 1 << 10: + sig = "k"; + break; /* low speed */ + case 2 << 10: + sig = "j"; + break; + default: + sig = "?"; + break; + } + + return scnprintf(buf, len, + "%s%sport:%d status %06x %d " + "sig=%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", port, status, + status>>25,/*device address */ + sig, + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", + (status & PORT_PEC) ? " PEC" : "", + (status & PORT_PE) ? " PE" : "", + (status & PORT_CSC) ? " CSC" : "", + (status & PORT_CONNECT) ? " CONNECT" : ""); +} + +#else +static inline void __maybe_unused +dbg_qh(char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{} + +static inline int __maybe_unused +dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +{ return 0; } + +static inline int __maybe_unused +dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) +{ return 0; } + +static inline int __maybe_unused +dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +{ return 0; } + +static inline int __maybe_unused +dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) +{ return 0; } + +#endif /* DEBUG */ + +/* functions have the "wrong" filename when they're output... */ +#define dbg_status(fotg210, label, status) { \ + char _buf[80]; \ + dbg_status_buf(_buf, sizeof(_buf), label, status); \ + fotg210_dbg(fotg210, "%s\n", _buf); \ +} + +#define dbg_cmd(fotg210, label, command) { \ + char _buf[80]; \ + dbg_command_buf(_buf, sizeof(_buf), label, command); \ + fotg210_dbg(fotg210, "%s\n", _buf); \ +} + +#define dbg_port(fotg210, label, port, status) { \ + char _buf[80]; \ + dbg_port_buf(_buf, sizeof(_buf), label, port, status); \ + fotg210_dbg(fotg210, "%s\n", _buf); \ +} + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILES + +static inline void create_debug_files(struct fotg210_hcd *bus) { } +static inline void remove_debug_files(struct fotg210_hcd *bus) { } + +#else + +/* troubleshooting help: expose state in debugfs */ + +static int debug_async_open(struct inode *, struct file *); +static int debug_periodic_open(struct inode *, struct file *); +static int debug_registers_open(struct inode *, struct file *); +static int debug_async_open(struct inode *, struct file *); + +static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); +static int debug_close(struct inode *, struct file *); + +static const struct file_operations debug_async_fops = { + .owner = THIS_MODULE, + .open = debug_async_open, + .read = debug_output, + .release = debug_close, + .llseek = default_llseek, +}; +static const struct file_operations debug_periodic_fops = { + .owner = THIS_MODULE, + .open = debug_periodic_open, + .read = debug_output, + .release = debug_close, + .llseek = default_llseek, +}; +static const struct file_operations debug_registers_fops = { + .owner = THIS_MODULE, + .open = debug_registers_open, + .read = debug_output, + .release = debug_close, + .llseek = default_llseek, +}; + +static struct dentry *fotg210_debug_root; + +struct debug_buffer { + ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ + struct usb_bus *bus; + struct mutex mutex; /* protect filling of buffer */ + size_t count; /* number of characters filled into buffer */ + char *output_buf; + size_t alloc_size; +}; + +#define speed_char(info1)({ char tmp; \ + switch (info1 & (3 << 12)) { \ + case QH_FULL_SPEED: \ + tmp = 'f'; break; \ + case QH_LOW_SPEED: \ + tmp = 'l'; break; \ + case QH_HIGH_SPEED: \ + tmp = 'h'; break; \ + default: \ + tmp = '?'; break; \ + }; tmp; }) + +static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) +{ + __u32 v = hc32_to_cpu(fotg210, token); + + if (v & QTD_STS_ACTIVE) + return '*'; + if (v & QTD_STS_HALT) + return '-'; + if (!IS_SHORT_READ(v)) + return ' '; + /* tries to advance through hw_alt_next */ + return '/'; +} + +static void qh_lines( + struct fotg210_hcd *fotg210, + struct fotg210_qh *qh, + char **nextp, + unsigned *sizep +) +{ + u32 scratch; + u32 hw_curr; + struct fotg210_qtd *td; + unsigned temp; + unsigned size = *sizep; + char *next = *nextp; + char mark; + __le32 list_end = FOTG210_LIST_END(fotg210); + struct fotg210_qh_hw *hw = qh->hw; + + if (hw->hw_qtd_next == list_end) /* NEC does this */ + mark = '@'; + else + mark = token_mark(fotg210, hw->hw_token); + if (mark == '/') { /* qh_alt_next controls qh advance? */ + if ((hw->hw_alt_next & QTD_MASK(fotg210)) + == fotg210->async->hw->hw_alt_next) + mark = '#'; /* blocked */ + else if (hw->hw_alt_next == list_end) + mark = '.'; /* use hw_qtd_next */ + /* else alt_next points to some other qtd */ + } + scratch = hc32_to_cpup(fotg210, &hw->hw_info1); + hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0; + temp = scnprintf(next, size, + "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)", + qh, scratch & 0x007f, + speed_char(scratch), + (scratch >> 8) & 0x000f, + scratch, hc32_to_cpup(fotg210, &hw->hw_info2), + hc32_to_cpup(fotg210, &hw->hw_token), mark, + (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token) + ? "data1" : "data0", + (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f); + size -= temp; + next += temp; + + /* hc may be modifying the list as we read it ... */ + list_for_each_entry(td, &qh->qtd_list, qtd_list) { + scratch = hc32_to_cpup(fotg210, &td->hw_token); + mark = ' '; + if (hw_curr == td->qtd_dma) + mark = '*'; + else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma)) + mark = '+'; + else if (QTD_LENGTH(scratch)) { + if (td->hw_alt_next == fotg210->async->hw->hw_alt_next) + mark = '#'; + else if (td->hw_alt_next != list_end) + mark = '/'; + } + temp = snprintf(next, size, + "\n\t%p%c%s len=%d %08x urb %p", + td, mark, ({ char *tmp; + switch ((scratch>>8)&0x03) { + case 0: + tmp = "out"; + break; + case 1: + tmp = "in"; + break; + case 2: + tmp = "setup"; + break; + default: + tmp = "?"; + break; + } tmp; }), + (scratch >> 16) & 0x7fff, + scratch, + td->urb); + if (size < temp) + temp = size; + size -= temp; + next += temp; + if (temp == size) + goto done; + } + + temp = snprintf(next, size, "\n"); + if (size < temp) + temp = size; + size -= temp; + next += temp; + +done: + *sizep = size; + *nextp = next; +} + +static ssize_t fill_async_buffer(struct debug_buffer *buf) +{ + struct usb_hcd *hcd; + struct fotg210_hcd *fotg210; + unsigned long flags; + unsigned temp, size; + char *next; + struct fotg210_qh *qh; + + hcd = bus_to_hcd(buf->bus); + fotg210 = hcd_to_fotg210(hcd); + next = buf->output_buf; + size = buf->alloc_size; + + *next = 0; + + /* dumps a snapshot of the async schedule. + * usually empty except for long-term bulk reads, or head. + * one QH per line, and TDs we know about + */ + spin_lock_irqsave(&fotg210->lock, flags); + for (qh = fotg210->async->qh_next.qh; size > 0 && qh; + qh = qh->qh_next.qh) + qh_lines(fotg210, qh, &next, &size); + if (fotg210->async_unlink && size > 0) { + temp = scnprintf(next, size, "\nunlink =\n"); + size -= temp; + next += temp; + + for (qh = fotg210->async_unlink; size > 0 && qh; + qh = qh->unlink_next) + qh_lines(fotg210, qh, &next, &size); + } + spin_unlock_irqrestore(&fotg210->lock, flags); + + return strlen(buf->output_buf); +} + +#define DBG_SCHED_LIMIT 64 +static ssize_t fill_periodic_buffer(struct debug_buffer *buf) +{ + struct usb_hcd *hcd; + struct fotg210_hcd *fotg210; + unsigned long flags; + union fotg210_shadow p, *seen; + unsigned temp, size, seen_count; + char *next; + unsigned i; + __hc32 tag; + + seen = kmalloc(DBG_SCHED_LIMIT * sizeof(*seen), GFP_ATOMIC); + if (!seen) + return 0; + seen_count = 0; + + hcd = bus_to_hcd(buf->bus); + fotg210 = hcd_to_fotg210(hcd); + next = buf->output_buf; + size = buf->alloc_size; + + temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size); + size -= temp; + next += temp; + + /* dump a snapshot of the periodic schedule. + * iso changes, interrupt usually doesn't. + */ + spin_lock_irqsave(&fotg210->lock, flags); + for (i = 0; i < fotg210->periodic_size; i++) { + p = fotg210->pshadow[i]; + if (likely(!p.ptr)) + continue; + tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]); + + temp = scnprintf(next, size, "%4d: ", i); + size -= temp; + next += temp; + + do { + struct fotg210_qh_hw *hw; + + switch (hc32_to_cpu(fotg210, tag)) { + case Q_TYPE_QH: + hw = p.qh->hw; + temp = scnprintf(next, size, " qh%d-%04x/%p", + p.qh->period, + hc32_to_cpup(fotg210, + &hw->hw_info2) + /* uframe masks */ + & (QH_CMASK | QH_SMASK), + p.qh); + size -= temp; + next += temp; + /* don't repeat what follows this qh */ + for (temp = 0; temp < seen_count; temp++) { + if (seen[temp].ptr != p.ptr) + continue; + if (p.qh->qh_next.ptr) { + temp = scnprintf(next, size, + " ..."); + size -= temp; + next += temp; + } + break; + } + /* show more info the first time around */ + if (temp == seen_count) { + u32 scratch = hc32_to_cpup(fotg210, + &hw->hw_info1); + struct fotg210_qtd *qtd; + char *type = ""; + + /* count tds, get ep direction */ + temp = 0; + list_for_each_entry(qtd, + &p.qh->qtd_list, + qtd_list) { + temp++; + switch (0x03 & (hc32_to_cpu( + fotg210, + qtd->hw_token) >> 8)) { + case 0: + type = "out"; + continue; + case 1: + type = "in"; + continue; + } + } + + temp = scnprintf(next, size, + "(%c%d ep%d%s " + "[%d/%d] q%d p%d)", + speed_char(scratch), + scratch & 0x007f, + (scratch >> 8) & 0x000f, type, + p.qh->usecs, p.qh->c_usecs, + temp, + 0x7ff & (scratch >> 16)); + + if (seen_count < DBG_SCHED_LIMIT) + seen[seen_count++].qh = p.qh; + } else + temp = 0; + tag = Q_NEXT_TYPE(fotg210, hw->hw_next); + p = p.qh->qh_next; + break; + case Q_TYPE_FSTN: + temp = scnprintf(next, size, + " fstn-%8x/%p", p.fstn->hw_prev, + p.fstn); + tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next); + p = p.fstn->fstn_next; + break; + case Q_TYPE_ITD: + temp = scnprintf(next, size, + " itd/%p", p.itd); + tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); + p = p.itd->itd_next; + break; + } + size -= temp; + next += temp; + } while (p.ptr); + + temp = scnprintf(next, size, "\n"); + size -= temp; + next += temp; + } + spin_unlock_irqrestore(&fotg210->lock, flags); + kfree(seen); + + return buf->alloc_size - size; +} +#undef DBG_SCHED_LIMIT + +static const char *rh_state_string(struct fotg210_hcd *fotg210) +{ + switch (fotg210->rh_state) { + case FOTG210_RH_HALTED: + return "halted"; + case FOTG210_RH_SUSPENDED: + return "suspended"; + case FOTG210_RH_RUNNING: + return "running"; + case FOTG210_RH_STOPPING: + return "stopping"; + } + return "?"; +} + +static ssize_t fill_registers_buffer(struct debug_buffer *buf) +{ + struct usb_hcd *hcd; + struct fotg210_hcd *fotg210; + unsigned long flags; + unsigned temp, size, i; + char *next, scratch[80]; + static const char fmt[] = "%*s\n"; + static const char label[] = ""; + + hcd = bus_to_hcd(buf->bus); + fotg210 = hcd_to_fotg210(hcd); + next = buf->output_buf; + size = buf->alloc_size; + + spin_lock_irqsave(&fotg210->lock, flags); + + if (!HCD_HW_ACCESSIBLE(hcd)) { + size = scnprintf(next, size, + "bus %s, device %s\n" + "%s\n" + "SUSPENDED(no register access)\n", + hcd->self.controller->bus->name, + dev_name(hcd->self.controller), + hcd->product_desc); + goto done; + } + + /* Capability Registers */ + i = HC_VERSION(fotg210, fotg210_readl(fotg210, + &fotg210->caps->hc_capbase)); + temp = scnprintf(next, size, + "bus %s, device %s\n" + "%s\n" + "EHCI %x.%02x, rh state %s\n", + hcd->self.controller->bus->name, + dev_name(hcd->self.controller), + hcd->product_desc, + i >> 8, i & 0x0ff, rh_state_string(fotg210)); + size -= temp; + next += temp; + + /* FIXME interpret both types of params */ + i = fotg210_readl(fotg210, &fotg210->caps->hcs_params); + temp = scnprintf(next, size, "structural params 0x%08x\n", i); + size -= temp; + next += temp; + + i = fotg210_readl(fotg210, &fotg210->caps->hcc_params); + temp = scnprintf(next, size, "capability params 0x%08x\n", i); + size -= temp; + next += temp; + + /* Operational Registers */ + temp = dbg_status_buf(scratch, sizeof(scratch), label, + fotg210_readl(fotg210, &fotg210->regs->status)); + temp = scnprintf(next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_command_buf(scratch, sizeof(scratch), label, + fotg210_readl(fotg210, &fotg210->regs->command)); + temp = scnprintf(next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_intr_buf(scratch, sizeof(scratch), label, + fotg210_readl(fotg210, &fotg210->regs->intr_enable)); + temp = scnprintf(next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = scnprintf(next, size, "uframe %04x\n", + fotg210_read_frame_index(fotg210)); + size -= temp; + next += temp; + + if (fotg210->async_unlink) { + temp = scnprintf(next, size, "async unlink qh %p\n", + fotg210->async_unlink); + size -= temp; + next += temp; + } + +#ifdef FOTG210_STATS + temp = scnprintf(next, size, + "irq normal %ld err %ld iaa %ld(lost %ld)\n", + fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa, + fotg210->stats.lost_iaa); + size -= temp; + next += temp; + + temp = scnprintf(next, size, "complete %ld unlink %ld\n", + fotg210->stats.complete, fotg210->stats.unlink); + size -= temp; + next += temp; +#endif + +done: + spin_unlock_irqrestore(&fotg210->lock, flags); + + return buf->alloc_size - size; +} + +static struct debug_buffer *alloc_buffer(struct usb_bus *bus, + ssize_t (*fill_func)(struct debug_buffer *)) +{ + struct debug_buffer *buf; + + buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); + + if (buf) { + buf->bus = bus; + buf->fill_func = fill_func; + mutex_init(&buf->mutex); + buf->alloc_size = PAGE_SIZE; + } + + return buf; +} + +static int fill_buffer(struct debug_buffer *buf) +{ + int ret = 0; + + if (!buf->output_buf) + buf->output_buf = vmalloc(buf->alloc_size); + + if (!buf->output_buf) { + ret = -ENOMEM; + goto out; + } + + ret = buf->fill_func(buf); + + if (ret >= 0) { + buf->count = ret; + ret = 0; + } + +out: + return ret; +} + +static ssize_t debug_output(struct file *file, char __user *user_buf, + size_t len, loff_t *offset) +{ + struct debug_buffer *buf = file->private_data; + int ret = 0; + + mutex_lock(&buf->mutex); + if (buf->count == 0) { + ret = fill_buffer(buf); + if (ret != 0) { + mutex_unlock(&buf->mutex); + goto out; + } + } + mutex_unlock(&buf->mutex); + + ret = simple_read_from_buffer(user_buf, len, offset, + buf->output_buf, buf->count); + +out: + return ret; + +} + +static int debug_close(struct inode *inode, struct file *file) +{ + struct debug_buffer *buf = file->private_data; + + if (buf) { + vfree(buf->output_buf); + kfree(buf); + } + + return 0; +} +static int debug_async_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + +static int debug_periodic_open(struct inode *inode, struct file *file) +{ + struct debug_buffer *buf; + buf = alloc_buffer(inode->i_private, fill_periodic_buffer); + if (!buf) + return -ENOMEM; + + buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; + file->private_data = buf; + return 0; +} + +static int debug_registers_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, + fill_registers_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + +static inline void create_debug_files(struct fotg210_hcd *fotg210) +{ + struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; + + fotg210->debug_dir = debugfs_create_dir(bus->bus_name, + fotg210_debug_root); + if (!fotg210->debug_dir) + return; + + if (!debugfs_create_file("async", S_IRUGO, fotg210->debug_dir, bus, + &debug_async_fops)) + goto file_error; + + if (!debugfs_create_file("periodic", S_IRUGO, fotg210->debug_dir, bus, + &debug_periodic_fops)) + goto file_error; + + if (!debugfs_create_file("registers", S_IRUGO, fotg210->debug_dir, bus, + &debug_registers_fops)) + goto file_error; + + return; + +file_error: + debugfs_remove_recursive(fotg210->debug_dir); +} + +static inline void remove_debug_files(struct fotg210_hcd *fotg210) +{ + debugfs_remove_recursive(fotg210->debug_dir); +} + +#endif /* STUB_DEBUG_FILES */ +/*-------------------------------------------------------------------------*/ + +/* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, + u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = fotg210_readl(fotg210, ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +/* + * Force HC to halt state from unknown (EHCI spec section 2.3). + * Must be called with interrupts enabled and the lock not held. + */ +static int fotg210_halt(struct fotg210_hcd *fotg210) +{ + u32 temp; + + spin_lock_irq(&fotg210->lock); + + /* disable any irqs left enabled by previous code */ + fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); + + /* + * This routine gets called during probe before fotg210->command + * has been initialized, so we can't rely on its value. + */ + fotg210->command &= ~CMD_RUN; + temp = fotg210_readl(fotg210, &fotg210->regs->command); + temp &= ~(CMD_RUN | CMD_IAAD); + fotg210_writel(fotg210, temp, &fotg210->regs->command); + + spin_unlock_irq(&fotg210->lock); + synchronize_irq(fotg210_to_hcd(fotg210)->irq); + + return handshake(fotg210, &fotg210->regs->status, + STS_HALT, STS_HALT, 16 * 125); +} + +/* + * Reset a non-running (STS_HALT == 1) controller. + * Must be called with interrupts enabled and the lock not held. + */ +static int fotg210_reset(struct fotg210_hcd *fotg210) +{ + int retval; + u32 command = fotg210_readl(fotg210, &fotg210->regs->command); + + /* If the EHCI debug controller is active, special care must be + * taken before and after a host controller reset */ + if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210))) + fotg210->debug = NULL; + + command |= CMD_RESET; + dbg_cmd(fotg210, "reset", command); + fotg210_writel(fotg210, command, &fotg210->regs->command); + fotg210->rh_state = FOTG210_RH_HALTED; + fotg210->next_statechange = jiffies; + retval = handshake(fotg210, &fotg210->regs->command, + CMD_RESET, 0, 250 * 1000); + + if (retval) + return retval; + + if (fotg210->debug) + dbgp_external_startup(fotg210_to_hcd(fotg210)); + + fotg210->port_c_suspend = fotg210->suspended_ports = + fotg210->resuming_ports = 0; + return retval; +} + +/* + * Idle the controller (turn off the schedules). + * Must be called with interrupts enabled and the lock not held. + */ +static void fotg210_quiesce(struct fotg210_hcd *fotg210) +{ + u32 temp; + + if (fotg210->rh_state != FOTG210_RH_RUNNING) + return; + + /* wait for any schedule enables/disables to take effect */ + temp = (fotg210->command << 10) & (STS_ASS | STS_PSS); + handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp, + 16 * 125); + + /* then disable anything that's still active */ + spin_lock_irq(&fotg210->lock); + fotg210->command &= ~(CMD_ASE | CMD_PSE); + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + spin_unlock_irq(&fotg210->lock); + + /* hardware can take 16 microframes to turn off ... */ + handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0, + 16 * 125); +} + +/*-------------------------------------------------------------------------*/ + +static void end_unlink_async(struct fotg210_hcd *fotg210); +static void unlink_empty_async(struct fotg210_hcd *fotg210); +static void fotg210_work(struct fotg210_hcd *fotg210); +static void start_unlink_intr(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh); +static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); + +/*-------------------------------------------------------------------------*/ + +/* Set a bit in the USBCMD register */ +static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) +{ + fotg210->command |= bit; + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + + /* unblock posted write */ + fotg210_readl(fotg210, &fotg210->regs->command); +} + +/* Clear a bit in the USBCMD register */ +static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) +{ + fotg210->command &= ~bit; + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + + /* unblock posted write */ + fotg210_readl(fotg210, &fotg210->regs->command); +} + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI timer support... Now using hrtimers. + * + * Lots of different events are triggered from fotg210->hrtimer. Whenever + * the timer routine runs, it checks each possible event; events that are + * currently enabled and whose expiration time has passed get handled. + * The set of enabled events is stored as a collection of bitflags in + * fotg210->enabled_hrtimer_events, and they are numbered in order of + * increasing delay values (ranging between 1 ms and 100 ms). + * + * Rather than implementing a sorted list or tree of all pending events, + * we keep track only of the lowest-numbered pending event, in + * fotg210->next_hrtimer_event. Whenever fotg210->hrtimer gets restarted, its + * expiration time is set to the timeout value for this event. + * + * As a result, events might not get handled right away; the actual delay + * could be anywhere up to twice the requested delay. This doesn't + * matter, because none of the events are especially time-critical. The + * ones that matter most all have a delay of 1 ms, so they will be + * handled after 2 ms at most, which is okay. In addition to this, we + * allow for an expiration range of 1 ms. + */ + +/* + * Delay lengths for the hrtimer event types. + * Keep this list sorted by delay length, in the same order as + * the event types indexed by enum fotg210_hrtimer_event in fotg210.h. + */ +static unsigned event_delays_ns[] = { + 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */ + 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */ + 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */ + 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */ + 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */ + 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ + 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */ + 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ + 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */ + 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */ +}; + +/* Enable a pending hrtimer event */ +static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, + bool resched) +{ + ktime_t *timeout = &fotg210->hr_timeouts[event]; + + if (resched) + *timeout = ktime_add(ktime_get(), + ktime_set(0, event_delays_ns[event])); + fotg210->enabled_hrtimer_events |= (1 << event); + + /* Track only the lowest-numbered pending event */ + if (event < fotg210->next_hrtimer_event) { + fotg210->next_hrtimer_event = event; + hrtimer_start_range_ns(&fotg210->hrtimer, *timeout, + NSEC_PER_MSEC, HRTIMER_MODE_ABS); + } +} + + +/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ +static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) +{ + unsigned actual, want; + + /* Don't enable anything if the controller isn't running (e.g., died) */ + if (fotg210->rh_state != FOTG210_RH_RUNNING) + return; + + want = (fotg210->command & CMD_ASE) ? STS_ASS : 0; + actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS; + + if (want != actual) { + + /* Poll again later, but give up after about 20 ms */ + if (fotg210->ASS_poll_count++ < 20) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS, + true); + return; + } + fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n", + want, actual); + } + fotg210->ASS_poll_count = 0; + + /* The status is up-to-date; restart or stop the schedule as needed */ + if (want == 0) { /* Stopped */ + if (fotg210->async_count > 0) + fotg210_set_command_bit(fotg210, CMD_ASE); + + } else { /* Running */ + if (fotg210->async_count == 0) { + + /* Turn off the schedule after a while */ + fotg210_enable_event(fotg210, + FOTG210_HRTIMER_DISABLE_ASYNC, + true); + } + } +} + +/* Turn off the async schedule after a brief delay */ +static void fotg210_disable_ASE(struct fotg210_hcd *fotg210) +{ + fotg210_clear_command_bit(fotg210, CMD_ASE); +} + + +/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ +static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) +{ + unsigned actual, want; + + /* Don't do anything if the controller isn't running (e.g., died) */ + if (fotg210->rh_state != FOTG210_RH_RUNNING) + return; + + want = (fotg210->command & CMD_PSE) ? STS_PSS : 0; + actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS; + + if (want != actual) { + + /* Poll again later, but give up after about 20 ms */ + if (fotg210->PSS_poll_count++ < 20) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS, + true); + return; + } + fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n", + want, actual); + } + fotg210->PSS_poll_count = 0; + + /* The status is up-to-date; restart or stop the schedule as needed */ + if (want == 0) { /* Stopped */ + if (fotg210->periodic_count > 0) + fotg210_set_command_bit(fotg210, CMD_PSE); + + } else { /* Running */ + if (fotg210->periodic_count == 0) { + + /* Turn off the schedule after a while */ + fotg210_enable_event(fotg210, + FOTG210_HRTIMER_DISABLE_PERIODIC, + true); + } + } +} + +/* Turn off the periodic schedule after a brief delay */ +static void fotg210_disable_PSE(struct fotg210_hcd *fotg210) +{ + fotg210_clear_command_bit(fotg210, CMD_PSE); +} + + +/* Poll the STS_HALT status bit; see when a dead controller stops */ +static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) +{ + if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) { + + /* Give up after a few milliseconds */ + if (fotg210->died_poll_count++ < 5) { + /* Try again later */ + fotg210_enable_event(fotg210, + FOTG210_HRTIMER_POLL_DEAD, true); + return; + } + fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n"); + } + + /* Clean up the mess */ + fotg210->rh_state = FOTG210_RH_HALTED; + fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); + fotg210_work(fotg210); + end_unlink_async(fotg210); + + /* Not in process context, so don't try to reset the controller */ +} + + +/* Handle unlinked interrupt QHs once they are gone from the hardware */ +static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) +{ + bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); + + /* + * Process all the QHs on the intr_unlink list that were added + * before the current unlink cycle began. The list is in + * temporal order, so stop when we reach the first entry in the + * current cycle. But if the root hub isn't running then + * process all the QHs on the list. + */ + fotg210->intr_unlinking = true; + while (fotg210->intr_unlink) { + struct fotg210_qh *qh = fotg210->intr_unlink; + + if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle) + break; + fotg210->intr_unlink = qh->unlink_next; + qh->unlink_next = NULL; + end_unlink_intr(fotg210, qh); + } + + /* Handle remaining entries later */ + if (fotg210->intr_unlink) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, + true); + ++fotg210->intr_unlink_cycle; + } + fotg210->intr_unlinking = false; +} + + +/* Start another free-iTDs/siTDs cycle */ +static void start_free_itds(struct fotg210_hcd *fotg210) +{ + if (!(fotg210->enabled_hrtimer_events & + BIT(FOTG210_HRTIMER_FREE_ITDS))) { + fotg210->last_itd_to_free = list_entry( + fotg210->cached_itd_list.prev, + struct fotg210_itd, itd_list); + fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true); + } +} + +/* Wait for controller to stop using old iTDs and siTDs */ +static void end_free_itds(struct fotg210_hcd *fotg210) +{ + struct fotg210_itd *itd, *n; + + if (fotg210->rh_state < FOTG210_RH_RUNNING) + fotg210->last_itd_to_free = NULL; + + list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) { + list_del(&itd->itd_list); + dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma); + if (itd == fotg210->last_itd_to_free) + break; + } + + if (!list_empty(&fotg210->cached_itd_list)) + start_free_itds(fotg210); +} + + +/* Handle lost (or very late) IAA interrupts */ +static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) +{ + if (fotg210->rh_state != FOTG210_RH_RUNNING) + return; + + /* + * Lost IAA irqs wedge things badly; seen first with a vt8235. + * So we need this watchdog, but must protect it against both + * (a) SMP races against real IAA firing and retriggering, and + * (b) clean HC shutdown, when IAA watchdog was pending. + */ + if (fotg210->async_iaa) { + u32 cmd, status; + + /* If we get here, IAA is *REALLY* late. It's barely + * conceivable that the system is so busy that CMD_IAAD + * is still legitimately set, so let's be sure it's + * clear before we read STS_IAA. (The HC should clear + * CMD_IAAD when it sets STS_IAA.) + */ + cmd = fotg210_readl(fotg210, &fotg210->regs->command); + + /* + * If IAA is set here it either legitimately triggered + * after the watchdog timer expired (_way_ late, so we'll + * still count it as lost) ... or a silicon erratum: + * - VIA seems to set IAA without triggering the IRQ; + * - IAAD potentially cleared without setting IAA. + */ + status = fotg210_readl(fotg210, &fotg210->regs->status); + if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { + COUNT(fotg210->stats.lost_iaa); + fotg210_writel(fotg210, STS_IAA, + &fotg210->regs->status); + } + + fotg210_vdbg(fotg210, "IAA watchdog: status %x cmd %x\n", + status, cmd); + end_unlink_async(fotg210); + } +} + + +/* Enable the I/O watchdog, if appropriate */ +static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) +{ + /* Not needed if the controller isn't running or it's already enabled */ + if (fotg210->rh_state != FOTG210_RH_RUNNING || + (fotg210->enabled_hrtimer_events & + BIT(FOTG210_HRTIMER_IO_WATCHDOG))) + return; + + /* + * Isochronous transfers always need the watchdog. + * For other sorts we use it only if the flag is set. + */ + if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog && + fotg210->async_count + fotg210->intr_count > 0)) + fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG, + true); +} + + +/* + * Handler functions for the hrtimer event types. + * Keep this array in the same order as the event types indexed by + * enum fotg210_hrtimer_event in fotg210.h. + */ +static void (*event_handlers[])(struct fotg210_hcd *) = { + fotg210_poll_ASS, /* FOTG210_HRTIMER_POLL_ASS */ + fotg210_poll_PSS, /* FOTG210_HRTIMER_POLL_PSS */ + fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */ + fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */ + end_free_itds, /* FOTG210_HRTIMER_FREE_ITDS */ + unlink_empty_async, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ + fotg210_iaa_watchdog, /* FOTG210_HRTIMER_IAA_WATCHDOG */ + fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ + fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */ + fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */ +}; + +static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) +{ + struct fotg210_hcd *fotg210 = + container_of(t, struct fotg210_hcd, hrtimer); + ktime_t now; + unsigned long events; + unsigned long flags; + unsigned e; + + spin_lock_irqsave(&fotg210->lock, flags); + + events = fotg210->enabled_hrtimer_events; + fotg210->enabled_hrtimer_events = 0; + fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; + + /* + * Check each pending event. If its time has expired, handle + * the event; otherwise re-enable it. + */ + now = ktime_get(); + for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) { + if (now.tv64 >= fotg210->hr_timeouts[e].tv64) + event_handlers[e](fotg210); + else + fotg210_enable_event(fotg210, e, false); + } + + spin_unlock_irqrestore(&fotg210->lock, flags); + return HRTIMER_NORESTART; +} + +/*-------------------------------------------------------------------------*/ + +#define fotg210_bus_suspend NULL +#define fotg210_bus_resume NULL + +/*-------------------------------------------------------------------------*/ + +static int check_reset_complete( + struct fotg210_hcd *fotg210, + int index, + u32 __iomem *status_reg, + int port_status +) { + if (!(port_status & PORT_CONNECT)) + return port_status; + + /* if reset finished and it's still not enabled -- handoff */ + if (!(port_status & PORT_PE)) { + /* with integrated TT, there's nobody to hand it to! */ + fotg210_dbg(fotg210, + "Failed to enable port %d on root hub TT\n", + index+1); + return port_status; + } else { + fotg210_dbg(fotg210, "port %d reset complete, port enabled\n", + index + 1); + } + + return port_status; +} + +/*-------------------------------------------------------------------------*/ + + +/* build "status change" packet (one or two bytes) from HC registers */ + +static int +fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp, status; + u32 mask; + int retval = 1; + unsigned long flags; + + /* init status to no-changes */ + buf[0] = 0; + + /* Inform the core about resumes-in-progress by returning + * a non-zero value even if there are no status changes. + */ + status = fotg210->resuming_ports; + + mask = PORT_CSC | PORT_PEC; + /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */ + + /* no hub change reports (bit 0) for now (power, ...) */ + + /* port N changes (bit N)? */ + spin_lock_irqsave(&fotg210->lock, flags); + + temp = fotg210_readl(fotg210, &fotg210->regs->port_status); + + /* + * Return status information even for ports with OWNER set. + * Otherwise khubd wouldn't see the disconnect event when a + * high-speed device is switched over to the companion + * controller by the user. + */ + + if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) + || (fotg210->reset_done[0] && time_after_eq( + jiffies, fotg210->reset_done[0]))) { + buf[0] |= 1 << 1; + status = STS_PCD; + } + /* FIXME autosuspend idle root hubs */ + spin_unlock_irqrestore(&fotg210->lock, flags); + return status ? retval : 0; +} + +/*-------------------------------------------------------------------------*/ + +static void +fotg210_hub_descriptor( + struct fotg210_hcd *fotg210, + struct usb_hub_descriptor *desc +) { + int ports = HCS_N_PORTS(fotg210->hcs_params); + u16 temp; + + desc->bDescriptorType = 0x29; + desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */ + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = ports; + temp = 1 + (ports / 8); + desc->bDescLength = 7 + 2 * temp; + + /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ + memset(&desc->u.hs.DeviceRemovable[0], 0, temp); + memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); + + temp = 0x0008; /* per-port overcurrent reporting */ + temp |= 0x0002; /* no power switching */ + desc->wHubCharacteristics = cpu_to_le16(temp); +} + +/*-------------------------------------------------------------------------*/ + +static int fotg210_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) { + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + int ports = HCS_N_PORTS(fotg210->hcs_params); + u32 __iomem *status_reg = &fotg210->regs->port_status; + u32 temp, temp1, status; + unsigned long flags; + int retval = 0; + unsigned selector; + + /* + * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. + * HCS_INDICATOR may say we can change LEDs to off/amber/green. + * (track current state ourselves) ... blink for diagnostics, + * power, "this is the one", etc. EHCI spec supports this. + */ + + spin_lock_irqsave(&fotg210->lock, flags); + switch (typeReq) { + case ClearHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = fotg210_readl(fotg210, status_reg); + temp &= ~PORT_RWC_BITS; + + /* + * Even if OWNER is set, so the port is owned by the + * companion controller, khubd needs to be able to clear + * the port-change status bits (especially + * USB_PORT_STAT_C_CONNECTION). + */ + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + fotg210_writel(fotg210, temp & ~PORT_PE, status_reg); + break; + case USB_PORT_FEAT_C_ENABLE: + fotg210_writel(fotg210, temp | PORT_PEC, status_reg); + break; + case USB_PORT_FEAT_SUSPEND: + if (temp & PORT_RESET) + goto error; + if (!(temp & PORT_SUSPEND)) + break; + if ((temp & PORT_PE) == 0) + goto error; + + /* resume signaling for 20 msec */ + fotg210_writel(fotg210, temp | PORT_RESUME, status_reg); + fotg210->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + break; + case USB_PORT_FEAT_C_SUSPEND: + clear_bit(wIndex, &fotg210->port_c_suspend); + break; + case USB_PORT_FEAT_C_CONNECTION: + fotg210_writel(fotg210, temp | PORT_CSC, status_reg); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + fotg210_writel(fotg210, temp | OTGISR_OVC, + &fotg210->regs->otgisr); + break; + case USB_PORT_FEAT_C_RESET: + /* GetPortStatus clears reset */ + break; + default: + goto error; + } + fotg210_readl(fotg210, &fotg210->regs->command); + break; + case GetHubDescriptor: + fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *) + buf); + break; + case GetHubStatus: + /* no hub-wide feature/status flags */ + memset(buf, 0, 4); + /*cpu_to_le32s ((u32 *) buf); */ + break; + case GetPortStatus: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + status = 0; + temp = fotg210_readl(fotg210, status_reg); + + /* wPortChange bits */ + if (temp & PORT_CSC) + status |= USB_PORT_STAT_C_CONNECTION << 16; + if (temp & PORT_PEC) + status |= USB_PORT_STAT_C_ENABLE << 16; + + temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); + if (temp1 & OTGISR_OVC) + status |= USB_PORT_STAT_C_OVERCURRENT << 16; + + /* whoever resumes must GetPortStatus to complete it!! */ + if (temp & PORT_RESUME) { + + /* Remote Wakeup received? */ + if (!fotg210->reset_done[wIndex]) { + /* resume signaling for 20 msec */ + fotg210->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + /* check the port again */ + mod_timer(&fotg210_to_hcd(fotg210)->rh_timer, + fotg210->reset_done[wIndex]); + } + + /* resume completed? */ + else if (time_after_eq(jiffies, + fotg210->reset_done[wIndex])) { + clear_bit(wIndex, &fotg210->suspended_ports); + set_bit(wIndex, &fotg210->port_c_suspend); + fotg210->reset_done[wIndex] = 0; + + /* stop resume signaling */ + temp = fotg210_readl(fotg210, status_reg); + fotg210_writel(fotg210, + temp & ~(PORT_RWC_BITS | PORT_RESUME), + status_reg); + clear_bit(wIndex, &fotg210->resuming_ports); + retval = handshake(fotg210, status_reg, + PORT_RESUME, 0, 2000 /* 2msec */); + if (retval != 0) { + fotg210_err(fotg210, + "port %d resume error %d\n", + wIndex + 1, retval); + goto error; + } + temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); + } + } + + /* whoever resets must GetPortStatus to complete it!! */ + if ((temp & PORT_RESET) + && time_after_eq(jiffies, + fotg210->reset_done[wIndex])) { + status |= USB_PORT_STAT_C_RESET << 16; + fotg210->reset_done[wIndex] = 0; + clear_bit(wIndex, &fotg210->resuming_ports); + + /* force reset to complete */ + fotg210_writel(fotg210, + temp & ~(PORT_RWC_BITS | PORT_RESET), + status_reg); + /* REVISIT: some hardware needs 550+ usec to clear + * this bit; seems too long to spin routinely... + */ + retval = handshake(fotg210, status_reg, + PORT_RESET, 0, 1000); + if (retval != 0) { + fotg210_err(fotg210, "port %d reset error %d\n", + wIndex + 1, retval); + goto error; + } + + /* see what we found out */ + temp = check_reset_complete(fotg210, wIndex, status_reg, + fotg210_readl(fotg210, status_reg)); + } + + if (!(temp & (PORT_RESUME|PORT_RESET))) { + fotg210->reset_done[wIndex] = 0; + clear_bit(wIndex, &fotg210->resuming_ports); + } + + /* transfer dedicated ports to the companion hc */ + if ((temp & PORT_CONNECT) && + test_bit(wIndex, &fotg210->companion_ports)) { + temp &= ~PORT_RWC_BITS; + fotg210_writel(fotg210, temp, status_reg); + fotg210_dbg(fotg210, "port %d --> companion\n", + wIndex + 1); + temp = fotg210_readl(fotg210, status_reg); + } + + /* + * Even if OWNER is set, there's no harm letting khubd + * see the wPortStatus values (they should all be 0 except + * for PORT_POWER anyway). + */ + + if (temp & PORT_CONNECT) { + status |= USB_PORT_STAT_CONNECTION; + status |= fotg210_port_speed(fotg210, temp); + } + if (temp & PORT_PE) + status |= USB_PORT_STAT_ENABLE; + + /* maybe the port was unsuspended without our knowledge */ + if (temp & (PORT_SUSPEND|PORT_RESUME)) { + status |= USB_PORT_STAT_SUSPEND; + } else if (test_bit(wIndex, &fotg210->suspended_ports)) { + clear_bit(wIndex, &fotg210->suspended_ports); + clear_bit(wIndex, &fotg210->resuming_ports); + fotg210->reset_done[wIndex] = 0; + if (temp & PORT_PE) + set_bit(wIndex, &fotg210->port_c_suspend); + } + + temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); + if (temp1 & OTGISR_OVC) + status |= USB_PORT_STAT_OVERCURRENT; + if (temp & PORT_RESET) + status |= USB_PORT_STAT_RESET; + if (test_bit(wIndex, &fotg210->port_c_suspend)) + status |= USB_PORT_STAT_C_SUSPEND << 16; + +#ifndef VERBOSE_DEBUG + if (status & ~0xffff) /* only if wPortChange is interesting */ +#endif + dbg_port(fotg210, "GetStatus", wIndex + 1, temp); + put_unaligned_le32(status, buf); + break; + case SetHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case SetPortFeature: + selector = wIndex >> 8; + wIndex &= 0xff; + + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = fotg210_readl(fotg210, status_reg); + temp &= ~PORT_RWC_BITS; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if ((temp & PORT_PE) == 0 + || (temp & PORT_RESET) != 0) + goto error; + + /* After above check the port must be connected. + * Set appropriate bit thus could put phy into low power + * mode if we have hostpc feature + */ + fotg210_writel(fotg210, temp | PORT_SUSPEND, + status_reg); + set_bit(wIndex, &fotg210->suspended_ports); + break; + case USB_PORT_FEAT_RESET: + if (temp & PORT_RESUME) + goto error; + /* line status bits may report this as low speed, + * which can be fine if this root hub has a + * transaction translator built in. + */ + fotg210_vdbg(fotg210, "port %d reset\n", wIndex + 1); + temp |= PORT_RESET; + temp &= ~PORT_PE; + + /* + * caller must wait, then call GetPortStatus + * usb 2.0 spec says 50 ms resets on root + */ + fotg210->reset_done[wIndex] = jiffies + + msecs_to_jiffies(50); + fotg210_writel(fotg210, temp, status_reg); + break; + + /* For downstream facing ports (these): one hub port is put + * into test mode according to USB2 11.24.2.13, then the hub + * must be reset (which for root hub now means rmmod+modprobe, + * or else system reboot). See EHCI 2.3.9 and 4.14 for info + * about the EHCI-specific stuff. + */ + case USB_PORT_FEAT_TEST: + if (!selector || selector > 5) + goto error; + spin_unlock_irqrestore(&fotg210->lock, flags); + fotg210_quiesce(fotg210); + spin_lock_irqsave(&fotg210->lock, flags); + + /* Put all enabled ports into suspend */ + temp = fotg210_readl(fotg210, status_reg) & + ~PORT_RWC_BITS; + if (temp & PORT_PE) + fotg210_writel(fotg210, temp | PORT_SUSPEND, + status_reg); + + spin_unlock_irqrestore(&fotg210->lock, flags); + fotg210_halt(fotg210); + spin_lock_irqsave(&fotg210->lock, flags); + + temp = fotg210_readl(fotg210, status_reg); + temp |= selector << 16; + fotg210_writel(fotg210, temp, status_reg); + break; + + default: + goto error; + } + fotg210_readl(fotg210, &fotg210->regs->command); + break; + + default: +error: + /* "stall" on error */ + retval = -EPIPE; + } + spin_unlock_irqrestore(&fotg210->lock, flags); + return retval; +} + +static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd, + int portnum) +{ + return; +} + +static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, + int portnum) +{ + return 0; +} +/*-------------------------------------------------------------------------*/ +/* + * There's basically three types of memory: + * - data used only by the HCD ... kmalloc is fine + * - async and periodic schedules, shared by HC and HCD ... these + * need to use dma_pool or dma_alloc_coherent + * - driver buffers, read/written by HC ... single shot DMA mapped + * + * There's also "register" data (e.g. PCI or SOC), which is memory mapped. + * No memory seen by this driver is pageable. + */ + +/*-------------------------------------------------------------------------*/ + +/* Allocate the key transfer structures from the previously allocated pool */ + +static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, + struct fotg210_qtd *qtd, dma_addr_t dma) +{ + memset(qtd, 0, sizeof(*qtd)); + qtd->qtd_dma = dma; + qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); + qtd->hw_next = FOTG210_LIST_END(fotg210); + qtd->hw_alt_next = FOTG210_LIST_END(fotg210); + INIT_LIST_HEAD(&qtd->qtd_list); +} + +static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, + gfp_t flags) +{ + struct fotg210_qtd *qtd; + dma_addr_t dma; + + qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma); + if (qtd != NULL) + fotg210_qtd_init(fotg210, qtd, dma); + + return qtd; +} + +static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210, + struct fotg210_qtd *qtd) +{ + dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma); +} + + +static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + /* clean qtds first, and know this is not linked */ + if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { + fotg210_dbg(fotg210, "unused qh not empty!\n"); + BUG(); + } + if (qh->dummy) + fotg210_qtd_free(fotg210, qh->dummy); + dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); + kfree(qh); +} + +static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, + gfp_t flags) +{ + struct fotg210_qh *qh; + dma_addr_t dma; + + qh = kzalloc(sizeof(*qh), GFP_ATOMIC); + if (!qh) + goto done; + qh->hw = (struct fotg210_qh_hw *) + dma_pool_alloc(fotg210->qh_pool, flags, &dma); + if (!qh->hw) + goto fail; + memset(qh->hw, 0, sizeof(*qh->hw)); + qh->qh_dma = dma; + INIT_LIST_HEAD(&qh->qtd_list); + + /* dummy td enables safe urb queuing */ + qh->dummy = fotg210_qtd_alloc(fotg210, flags); + if (qh->dummy == NULL) { + fotg210_dbg(fotg210, "no dummy td\n"); + goto fail1; + } +done: + return qh; +fail1: + dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); +fail: + kfree(qh); + return NULL; +} + +/*-------------------------------------------------------------------------*/ + +/* The queue heads and transfer descriptors are managed from pools tied + * to each of the "per device" structures. + * This is the initialisation and cleanup code. + */ + +static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) +{ + if (fotg210->async) + qh_destroy(fotg210, fotg210->async); + fotg210->async = NULL; + + if (fotg210->dummy) + qh_destroy(fotg210, fotg210->dummy); + fotg210->dummy = NULL; + + /* DMA consistent memory and pools */ + if (fotg210->qtd_pool) + dma_pool_destroy(fotg210->qtd_pool); + fotg210->qtd_pool = NULL; + + if (fotg210->qh_pool) { + dma_pool_destroy(fotg210->qh_pool); + fotg210->qh_pool = NULL; + } + + if (fotg210->itd_pool) + dma_pool_destroy(fotg210->itd_pool); + fotg210->itd_pool = NULL; + + if (fotg210->periodic) + dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, + fotg210->periodic_size * sizeof(u32), + fotg210->periodic, fotg210->periodic_dma); + fotg210->periodic = NULL; + + /* shadow periodic table */ + kfree(fotg210->pshadow); + fotg210->pshadow = NULL; +} + +/* remember to add cleanup code (above) if you add anything here */ +static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) +{ + int i; + + /* QTDs for control/bulk/intr transfers */ + fotg210->qtd_pool = dma_pool_create("fotg210_qtd", + fotg210_to_hcd(fotg210)->self.controller, + sizeof(struct fotg210_qtd), + 32 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */); + if (!fotg210->qtd_pool) + goto fail; + + /* QHs for control/bulk/intr transfers */ + fotg210->qh_pool = dma_pool_create("fotg210_qh", + fotg210_to_hcd(fotg210)->self.controller, + sizeof(struct fotg210_qh_hw), + 32 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */); + if (!fotg210->qh_pool) + goto fail; + + fotg210->async = fotg210_qh_alloc(fotg210, flags); + if (!fotg210->async) + goto fail; + + /* ITD for high speed ISO transfers */ + fotg210->itd_pool = dma_pool_create("fotg210_itd", + fotg210_to_hcd(fotg210)->self.controller, + sizeof(struct fotg210_itd), + 64 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */); + if (!fotg210->itd_pool) + goto fail; + + /* Hardware periodic table */ + fotg210->periodic = (__le32 *) + dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, + fotg210->periodic_size * sizeof(__le32), + &fotg210->periodic_dma, 0); + if (fotg210->periodic == NULL) + goto fail; + + for (i = 0; i < fotg210->periodic_size; i++) + fotg210->periodic[i] = FOTG210_LIST_END(fotg210); + + /* software shadow of hardware table */ + fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *), + flags); + if (fotg210->pshadow != NULL) + return 0; + +fail: + fotg210_dbg(fotg210, "couldn't init memory\n"); + fotg210_mem_cleanup(fotg210); + return -ENOMEM; +} +/*-------------------------------------------------------------------------*/ +/* + * EHCI hardware queue manipulation ... the core. QH/QTD manipulation. + * + * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" + * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned + * buffers needed for the larger number). We use one QH per endpoint, queue + * multiple urbs (all three types) per endpoint. URBs may need several qtds. + * + * ISO traffic uses "ISO TD" (itd) records, and (along with + * interrupts) needs careful scheduling. Performance improvements can be + * an ongoing challenge. That's in "ehci-sched.c". + * + * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, + * or otherwise through transaction translators (TTs) in USB 2.0 hubs using + * (b) special fields in qh entries or (c) split iso entries. TTs will + * buffer low/full speed data so the host collects it at high speed. + */ + +/*-------------------------------------------------------------------------*/ + +/* fill a qtd, returning how much of the buffer we were able to queue up */ + +static int +qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, dma_addr_t buf, + size_t len, int token, int maxpacket) +{ + int i, count; + u64 addr = buf; + + /* one buffer entry per 4K ... first might be short or unaligned */ + qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr); + qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32)); + count = 0x1000 - (buf & 0x0fff); /* rest of that page */ + if (likely(len < count)) /* ... iff needed */ + count = len; + else { + buf += 0x1000; + buf &= ~0x0fff; + + /* per-qtd limit: from 16K to 20K (best alignment) */ + for (i = 1; count < len && i < 5; i++) { + addr = buf; + qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr); + qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210, + (u32)(addr >> 32)); + buf += 0x1000; + if ((count + 0x1000) < len) + count += 0x1000; + else + count = len; + } + + /* short packets may only terminate transfers */ + if (count != len) + count -= (count % maxpacket); + } + qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token); + qtd->length = count; + + return count; +} + +/*-------------------------------------------------------------------------*/ + +static inline void +qh_update(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, + struct fotg210_qtd *qtd) +{ + struct fotg210_qh_hw *hw = qh->hw; + + /* writes to an active overlay are unsafe */ + BUG_ON(qh->qh_state != QH_STATE_IDLE); + + hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma); + hw->hw_alt_next = FOTG210_LIST_END(fotg210); + + /* Except for control endpoints, we make hardware maintain data + * toggle (like OHCI) ... here (re)initialize the toggle in the QH, + * and set the pseudo-toggle in udev. Only usb_clear_halt() will + * ever clear it. + */ + if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) { + unsigned is_out, epnum; + + is_out = qh->is_out; + epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f; + if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { + hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE); + usb_settoggle(qh->dev, epnum, is_out, 1); + } + } + + hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING); +} + +/* if it weren't for a common silicon quirk (writing the dummy into the qh + * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault + * recovery (including urb dequeue) would need software changes to a QH... + */ +static void +qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + struct fotg210_qtd *qtd; + + if (list_empty(&qh->qtd_list)) + qtd = qh->dummy; + else { + qtd = list_entry(qh->qtd_list.next, + struct fotg210_qtd, qtd_list); + /* + * first qtd may already be partially processed. + * If we come here during unlink, the QH overlay region + * might have reference to the just unlinked qtd. The + * qtd is updated in qh_completions(). Update the QH + * overlay here. + */ + if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) { + qh->hw->hw_qtd_next = qtd->hw_next; + qtd = NULL; + } + } + + if (qtd) + qh_update(fotg210, qh, qtd); +} + +/*-------------------------------------------------------------------------*/ + +static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); + +static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_qh *qh = ep->hcpriv; + unsigned long flags; + + spin_lock_irqsave(&fotg210->lock, flags); + qh->clearing_tt = 0; + if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) + && fotg210->rh_state == FOTG210_RH_RUNNING) + qh_link_async(fotg210, qh); + spin_unlock_irqrestore(&fotg210->lock, flags); +} + +static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh, + struct urb *urb, u32 token) +{ + + /* If an async split transaction gets an error or is unlinked, + * the TT buffer may be left in an indeterminate state. We + * have to clear the TT buffer. + * + * Note: this routine is never called for Isochronous transfers. + */ + if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { +#ifdef DEBUG + struct usb_device *tt = urb->dev->tt->hub; + dev_dbg(&tt->dev, + "clear tt buffer port %d, a%d ep%d t%08x\n", + urb->dev->ttport, urb->dev->devnum, + usb_pipeendpoint(urb->pipe), token); +#endif /* DEBUG */ + if (urb->dev->tt->hub != + fotg210_to_hcd(fotg210)->self.root_hub) { + if (usb_hub_clear_tt_buffer(urb) == 0) + qh->clearing_tt = 1; + } + } +} + +static int qtd_copy_status( + struct fotg210_hcd *fotg210, + struct urb *urb, + size_t length, + u32 token +) +{ + int status = -EINPROGRESS; + + /* count IN/OUT bytes, not SETUP (even short packets) */ + if (likely(QTD_PID(token) != 2)) + urb->actual_length += length - QTD_LENGTH(token); + + /* don't modify error codes */ + if (unlikely(urb->unlinked)) + return status; + + /* force cleanup after short read; not always an error */ + if (unlikely(IS_SHORT_READ(token))) + status = -EREMOTEIO; + + /* serious "can't proceed" faults reported by the hardware */ + if (token & QTD_STS_HALT) { + if (token & QTD_STS_BABBLE) { + /* FIXME "must" disable babbling device's port too */ + status = -EOVERFLOW; + /* CERR nonzero + halt --> stall */ + } else if (QTD_CERR(token)) { + status = -EPIPE; + + /* In theory, more than one of the following bits can be set + * since they are sticky and the transaction is retried. + * Which to test first is rather arbitrary. + */ + } else if (token & QTD_STS_MMF) { + /* fs/ls interrupt xfer missed the complete-split */ + status = -EPROTO; + } else if (token & QTD_STS_DBE) { + status = (QTD_PID(token) == 1) /* IN ? */ + ? -ENOSR /* hc couldn't read data */ + : -ECOMM; /* hc couldn't write data */ + } else if (token & QTD_STS_XACT) { + /* timeout, bad CRC, wrong PID, etc */ + fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n", + urb->dev->devpath, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out"); + status = -EPROTO; + } else { /* unknown */ + status = -EPROTO; + } + + fotg210_vdbg(fotg210, + "dev%d ep%d%s qtd token %08x --> status %d\n", + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + token, status); + } + + return status; +} + +static void +fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, int status) +__releases(fotg210->lock) +__acquires(fotg210->lock) +{ + if (likely(urb->hcpriv != NULL)) { + struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; + + /* S-mask in a QH means it's an interrupt urb */ + if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) { + + /* ... update hc-wide periodic stats (for usbfs) */ + fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--; + } + } + + if (unlikely(urb->unlinked)) { + COUNT(fotg210->stats.unlink); + } else { + /* report non-error and short read status as zero */ + if (status == -EINPROGRESS || status == -EREMOTEIO) + status = 0; + COUNT(fotg210->stats.complete); + } + +#ifdef FOTG210_URB_TRACE + fotg210_dbg(fotg210, + "%s %s urb %p ep%d%s status %d len %d/%d\n", + __func__, urb->dev->devpath, urb, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + status, + urb->actual_length, urb->transfer_buffer_length); +#endif + + /* complete() can reenter this HCD */ + usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); + spin_unlock(&fotg210->lock); + usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status); + spin_lock(&fotg210->lock); +} + +static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); + +/* + * Process and free completed qtds for a qh, returning URBs to drivers. + * Chases up to qh->hw_current. Returns number of completions called, + * indicating how much "real" work we did. + */ +static unsigned +qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + struct fotg210_qtd *last, *end = qh->dummy; + struct list_head *entry, *tmp; + int last_status; + int stopped; + unsigned count = 0; + u8 state; + struct fotg210_qh_hw *hw = qh->hw; + + if (unlikely(list_empty(&qh->qtd_list))) + return count; + + /* completions (or tasks on other cpus) must never clobber HALT + * till we've gone through and cleaned everything up, even when + * they add urbs to this qh's queue or mark them for unlinking. + * + * NOTE: unlinking expects to be done in queue order. + * + * It's a bug for qh->qh_state to be anything other than + * QH_STATE_IDLE, unless our caller is scan_async() or + * scan_intr(). + */ + state = qh->qh_state; + qh->qh_state = QH_STATE_COMPLETING; + stopped = (state == QH_STATE_IDLE); + + rescan: + last = NULL; + last_status = -EINPROGRESS; + qh->needs_rescan = 0; + + /* remove de-activated QTDs from front of queue. + * after faults (including short reads), cleanup this urb + * then let the queue advance. + * if queue is stopped, handles unlinks. + */ + list_for_each_safe(entry, tmp, &qh->qtd_list) { + struct fotg210_qtd *qtd; + struct urb *urb; + u32 token = 0; + + qtd = list_entry(entry, struct fotg210_qtd, qtd_list); + urb = qtd->urb; + + /* clean up any state from previous QTD ...*/ + if (last) { + if (likely(last->urb != urb)) { + fotg210_urb_done(fotg210, last->urb, + last_status); + count++; + last_status = -EINPROGRESS; + } + fotg210_qtd_free(fotg210, last); + last = NULL; + } + + /* ignore urbs submitted during completions we reported */ + if (qtd == end) + break; + + /* hardware copies qtd out of qh overlay */ + rmb(); + token = hc32_to_cpu(fotg210, qtd->hw_token); + + /* always clean up qtds the hc de-activated */ + retry_xacterr: + if ((token & QTD_STS_ACTIVE) == 0) { + + /* Report Data Buffer Error: non-fatal but useful */ + if (token & QTD_STS_DBE) + fotg210_dbg(fotg210, + "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", + urb, + usb_endpoint_num(&urb->ep->desc), + usb_endpoint_dir_in(&urb->ep->desc) + ? "in" : "out", + urb->transfer_buffer_length, + qtd, + qh); + + /* on STALL, error, and short reads this urb must + * complete and all its qtds must be recycled. + */ + if ((token & QTD_STS_HALT) != 0) { + + /* retry transaction errors until we + * reach the software xacterr limit + */ + if ((token & QTD_STS_XACT) && + QTD_CERR(token) == 0 && + ++qh->xacterrs < QH_XACTERR_MAX && + !urb->unlinked) { + fotg210_dbg(fotg210, + "detected XactErr len %zu/%zu retry %d\n", + qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs); + + /* reset the token in the qtd and the + * qh overlay (which still contains + * the qtd) so that we pick up from + * where we left off + */ + token &= ~QTD_STS_HALT; + token |= QTD_STS_ACTIVE | + (FOTG210_TUNE_CERR << 10); + qtd->hw_token = cpu_to_hc32(fotg210, + token); + wmb(); + hw->hw_token = cpu_to_hc32(fotg210, + token); + goto retry_xacterr; + } + stopped = 1; + + /* magic dummy for some short reads; qh won't advance. + * that silicon quirk can kick in with this dummy too. + * + * other short reads won't stop the queue, including + * control transfers (status stage handles that) or + * most other single-qtd reads ... the queue stops if + * URB_SHORT_NOT_OK was set so the driver submitting + * the urbs could clean it up. + */ + } else if (IS_SHORT_READ(token) + && !(qtd->hw_alt_next + & FOTG210_LIST_END(fotg210))) { + stopped = 1; + } + + /* stop scanning when we reach qtds the hc is using */ + } else if (likely(!stopped + && fotg210->rh_state >= FOTG210_RH_RUNNING)) { + break; + + /* scan the whole queue for unlinks whenever it stops */ + } else { + stopped = 1; + + /* cancel everything if we halt, suspend, etc */ + if (fotg210->rh_state < FOTG210_RH_RUNNING) + last_status = -ESHUTDOWN; + + /* this qtd is active; skip it unless a previous qtd + * for its urb faulted, or its urb was canceled. + */ + else if (last_status == -EINPROGRESS && !urb->unlinked) + continue; + + /* qh unlinked; token in overlay may be most current */ + if (state == QH_STATE_IDLE + && cpu_to_hc32(fotg210, qtd->qtd_dma) + == hw->hw_current) { + token = hc32_to_cpu(fotg210, hw->hw_token); + + /* An unlink may leave an incomplete + * async transaction in the TT buffer. + * We have to clear it. + */ + fotg210_clear_tt_buffer(fotg210, qh, urb, + token); + } + } + + /* unless we already know the urb's status, collect qtd status + * and update count of bytes transferred. in common short read + * cases with only one data qtd (including control transfers), + * queue processing won't halt. but with two or more qtds (for + * example, with a 32 KB transfer), when the first qtd gets a + * short read the second must be removed by hand. + */ + if (last_status == -EINPROGRESS) { + last_status = qtd_copy_status(fotg210, urb, + qtd->length, token); + if (last_status == -EREMOTEIO + && (qtd->hw_alt_next + & FOTG210_LIST_END(fotg210))) + last_status = -EINPROGRESS; + + /* As part of low/full-speed endpoint-halt processing + * we must clear the TT buffer (11.17.5). + */ + if (unlikely(last_status != -EINPROGRESS && + last_status != -EREMOTEIO)) { + /* The TT's in some hubs malfunction when they + * receive this request following a STALL (they + * stop sending isochronous packets). Since a + * STALL can't leave the TT buffer in a busy + * state (if you believe Figures 11-48 - 11-51 + * in the USB 2.0 spec), we won't clear the TT + * buffer in this case. Strictly speaking this + * is a violation of the spec. + */ + if (last_status != -EPIPE) + fotg210_clear_tt_buffer(fotg210, qh, + urb, token); + } + } + + /* if we're removing something not at the queue head, + * patch the hardware queue pointer. + */ + if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { + last = list_entry(qtd->qtd_list.prev, + struct fotg210_qtd, qtd_list); + last->hw_next = qtd->hw_next; + } + + /* remove qtd; it's recycled after possible urb completion */ + list_del(&qtd->qtd_list); + last = qtd; + + /* reinit the xacterr counter for the next qtd */ + qh->xacterrs = 0; + } + + /* last urb's completion might still need calling */ + if (likely(last != NULL)) { + fotg210_urb_done(fotg210, last->urb, last_status); + count++; + fotg210_qtd_free(fotg210, last); + } + + /* Do we need to rescan for URBs dequeued during a giveback? */ + if (unlikely(qh->needs_rescan)) { + /* If the QH is already unlinked, do the rescan now. */ + if (state == QH_STATE_IDLE) + goto rescan; + + /* Otherwise we have to wait until the QH is fully unlinked. + * Our caller will start an unlink if qh->needs_rescan is + * set. But if an unlink has already started, nothing needs + * to be done. + */ + if (state != QH_STATE_LINKED) + qh->needs_rescan = 0; + } + + /* restore original state; caller must unlink or relink */ + qh->qh_state = state; + + /* be sure the hardware's done with the qh before refreshing + * it after fault cleanup, or recovering from silicon wrongly + * overlaying the dummy qtd (which reduces DMA chatter). + */ + if (stopped != 0 || hw->hw_qtd_next == FOTG210_LIST_END(fotg210)) { + switch (state) { + case QH_STATE_IDLE: + qh_refresh(fotg210, qh); + break; + case QH_STATE_LINKED: + /* We won't refresh a QH that's linked (after the HC + * stopped the queue). That avoids a race: + * - HC reads first part of QH; + * - CPU updates that first part and the token; + * - HC reads rest of that QH, including token + * Result: HC gets an inconsistent image, and then + * DMAs to/from the wrong memory (corrupting it). + * + * That should be rare for interrupt transfers, + * except maybe high bandwidth ... + */ + + /* Tell the caller to start an unlink */ + qh->needs_rescan = 1; + break; + /* otherwise, unlink already started */ + } + } + + return count; +} + +/*-------------------------------------------------------------------------*/ + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +/* ... and packet size, for any kind of endpoint descriptor */ +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + +/* + * reverse of qh_urb_transaction: free a list of TDs. + * used for cleanup after errors, before HC sees an URB's TDs. + */ +static void qtd_list_free( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *qtd_list +) { + struct list_head *entry, *temp; + + list_for_each_safe(entry, temp, qtd_list) { + struct fotg210_qtd *qtd; + + qtd = list_entry(entry, struct fotg210_qtd, qtd_list); + list_del(&qtd->qtd_list); + fotg210_qtd_free(fotg210, qtd); + } +} + +/* + * create a list of filled qtds for this URB; won't link into qh. + */ +static struct list_head * +qh_urb_transaction( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *head, + gfp_t flags +) { + struct fotg210_qtd *qtd, *qtd_prev; + dma_addr_t buf; + int len, this_sg_len, maxpacket; + int is_input; + u32 token; + int i; + struct scatterlist *sg; + + /* + * URBs map to sequences of QTDs: one logical transaction + */ + qtd = fotg210_qtd_alloc(fotg210, flags); + if (unlikely(!qtd)) + return NULL; + list_add_tail(&qtd->qtd_list, head); + qtd->urb = urb; + + token = QTD_STS_ACTIVE; + token |= (FOTG210_TUNE_CERR << 10); + /* for split transactions, SplitXState initialized to zero */ + + len = urb->transfer_buffer_length; + is_input = usb_pipein(urb->pipe); + if (usb_pipecontrol(urb->pipe)) { + /* SETUP pid */ + qtd_fill(fotg210, qtd, urb->setup_dma, + sizeof(struct usb_ctrlrequest), + token | (2 /* "setup" */ << 8), 8); + + /* ... and always at least one more pid */ + token ^= QTD_TOGGLE; + qtd_prev = qtd; + qtd = fotg210_qtd_alloc(fotg210, flags); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + + /* for zero length DATA stages, STATUS is always IN */ + if (len == 0) + token |= (1 /* "in" */ << 8); + } + + /* + * data transfer stage: buffer setup + */ + i = urb->num_mapped_sgs; + if (len > 0 && i > 0) { + sg = urb->sg; + buf = sg_dma_address(sg); + + /* urb->transfer_buffer_length may be smaller than the + * size of the scatterlist (or vice versa) + */ + this_sg_len = min_t(int, sg_dma_len(sg), len); + } else { + sg = NULL; + buf = urb->transfer_dma; + this_sg_len = len; + } + + if (is_input) + token |= (1 /* "in" */ << 8); + /* else it's already initted to "out" pid (0 << 8) */ + + maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); + + /* + * buffer gets wrapped in one or more qtds; + * last one may be "short" (including zero len) + * and may serve as a control status ack + */ + for (;;) { + int this_qtd_len; + + this_qtd_len = qtd_fill(fotg210, qtd, buf, this_sg_len, token, + maxpacket); + this_sg_len -= this_qtd_len; + len -= this_qtd_len; + buf += this_qtd_len; + + /* + * short reads advance to a "magic" dummy instead of the next + * qtd ... that forces the queue to stop, for manual cleanup. + * (this will usually be overridden later.) + */ + if (is_input) + qtd->hw_alt_next = fotg210->async->hw->hw_alt_next; + + /* qh makes control packets use qtd toggle; maybe switch it */ + if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) + token ^= QTD_TOGGLE; + + if (likely(this_sg_len <= 0)) { + if (--i <= 0 || len <= 0) + break; + sg = sg_next(sg); + buf = sg_dma_address(sg); + this_sg_len = min_t(int, sg_dma_len(sg), len); + } + + qtd_prev = qtd; + qtd = fotg210_qtd_alloc(fotg210, flags); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + } + + /* + * unless the caller requires manual cleanup after short reads, + * have the alt_next mechanism keep the queue running after the + * last data qtd (the only one, for control and most other cases). + */ + if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 + || usb_pipecontrol(urb->pipe))) + qtd->hw_alt_next = FOTG210_LIST_END(fotg210); + + /* + * control requests may need a terminating data "status" ack; + * other OUT ones may need a terminating short packet + * (zero length). + */ + if (likely(urb->transfer_buffer_length != 0)) { + int one_more = 0; + + if (usb_pipecontrol(urb->pipe)) { + one_more = 1; + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + } else if (usb_pipeout(urb->pipe) + && (urb->transfer_flags & URB_ZERO_PACKET) + && !(urb->transfer_buffer_length % maxpacket)) { + one_more = 1; + } + if (one_more) { + qtd_prev = qtd; + qtd = fotg210_qtd_alloc(fotg210, flags); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + + /* never any data in such packets */ + qtd_fill(fotg210, qtd, 0, 0, token, 0); + } + } + + /* by default, enable interrupt on urb completion */ + if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) + qtd->hw_token |= cpu_to_hc32(fotg210, QTD_IOC); + return head; + +cleanup: + qtd_list_free(fotg210, urb, head); + return NULL; +} + +/*-------------------------------------------------------------------------*/ +/* + * Would be best to create all qh's from config descriptors, + * when each interface/altsetting is established. Unlink + * any previous qh and cancel its urbs first; endpoints are + * implicitly reset then (data toggle too). + * That'd mean updating how usbcore talks to HCDs. (2.7?) +*/ + + +/* + * Each QH holds a qtd list; a QH is used for everything except iso. + * + * For interrupt urbs, the scheduler must set the microframe scheduling + * mask(s) each time the QH gets scheduled. For highspeed, that's + * just one microframe in the s-mask. For split interrupt transactions + * there are additional complications: c-mask, maybe FSTNs. + */ +static struct fotg210_qh * +qh_make( + struct fotg210_hcd *fotg210, + struct urb *urb, + gfp_t flags +) { + struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); + u32 info1 = 0, info2 = 0; + int is_input, type; + int maxp = 0; + struct usb_tt *tt = urb->dev->tt; + struct fotg210_qh_hw *hw; + + if (!qh) + return qh; + + /* + * init endpoint/device data for this QH + */ + info1 |= usb_pipeendpoint(urb->pipe) << 8; + info1 |= usb_pipedevice(urb->pipe) << 0; + + is_input = usb_pipein(urb->pipe); + type = usb_pipetype(urb->pipe); + maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input); + + /* 1024 byte maxpacket is a hardware ceiling. High bandwidth + * acts like up to 3KB, but is built from smaller packets. + */ + if (max_packet(maxp) > 1024) { + fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", + max_packet(maxp)); + goto done; + } + + /* Compute interrupt scheduling parameters just once, and save. + * - allowing for high bandwidth, how many nsec/uframe are used? + * - split transactions need a second CSPLIT uframe; same question + * - splits also need a schedule gap (for full/low speed I/O) + * - qh has a polling interval + * + * For control/bulk requests, the HC or TT handles these. + */ + if (type == PIPE_INTERRUPT) { + qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, + is_input, 0, + hb_mult(maxp) * max_packet(maxp))); + qh->start = NO_FRAME; + + if (urb->dev->speed == USB_SPEED_HIGH) { + qh->c_usecs = 0; + qh->gap_uf = 0; + + qh->period = urb->interval >> 3; + if (qh->period == 0 && urb->interval != 1) { + /* NOTE interval 2 or 4 uframes could work. + * But interval 1 scheduling is simpler, and + * includes high bandwidth. + */ + urb->interval = 1; + } else if (qh->period > fotg210->periodic_size) { + qh->period = fotg210->periodic_size; + urb->interval = qh->period << 3; + } + } else { + int think_time; + + /* gap is f(FS/LS transfer times) */ + qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, + is_input, 0, maxp) / (125 * 1000); + + /* FIXME this just approximates SPLIT/CSPLIT times */ + if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ + qh->c_usecs = qh->usecs + HS_USECS(0); + qh->usecs = HS_USECS(1); + } else { /* SPLIT+DATA, gap, CSPLIT */ + qh->usecs += HS_USECS(1); + qh->c_usecs = HS_USECS(0); + } + + think_time = tt ? tt->think_time : 0; + qh->tt_usecs = NS_TO_US(think_time + + usb_calc_bus_time(urb->dev->speed, + is_input, 0, max_packet(maxp))); + qh->period = urb->interval; + if (qh->period > fotg210->periodic_size) { + qh->period = fotg210->periodic_size; + urb->interval = qh->period; + } + } + } + + /* support for tt scheduling, and access to toggles */ + qh->dev = urb->dev; + + /* using TT? */ + switch (urb->dev->speed) { + case USB_SPEED_LOW: + info1 |= QH_LOW_SPEED; + /* FALL THROUGH */ + + case USB_SPEED_FULL: + /* EPS 0 means "full" */ + if (type != PIPE_INTERRUPT) + info1 |= (FOTG210_TUNE_RL_TT << 28); + if (type == PIPE_CONTROL) { + info1 |= QH_CONTROL_EP; /* for TT */ + info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ + } + info1 |= maxp << 16; + + info2 |= (FOTG210_TUNE_MULT_TT << 30); + + /* Some Freescale processors have an erratum in which the + * port number in the queue head was 0..N-1 instead of 1..N. + */ + if (fotg210_has_fsl_portno_bug(fotg210)) + info2 |= (urb->dev->ttport-1) << 23; + else + info2 |= urb->dev->ttport << 23; + + /* set the address of the TT; for TDI's integrated + * root hub tt, leave it zeroed. + */ + if (tt && tt->hub != fotg210_to_hcd(fotg210)->self.root_hub) + info2 |= tt->hub->devnum << 16; + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ + + break; + + case USB_SPEED_HIGH: /* no TT involved */ + info1 |= QH_HIGH_SPEED; + if (type == PIPE_CONTROL) { + info1 |= (FOTG210_TUNE_RL_HS << 28); + info1 |= 64 << 16; /* usb2 fixed maxpacket */ + info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ + info2 |= (FOTG210_TUNE_MULT_HS << 30); + } else if (type == PIPE_BULK) { + info1 |= (FOTG210_TUNE_RL_HS << 28); + /* The USB spec says that high speed bulk endpoints + * always use 512 byte maxpacket. But some device + * vendors decided to ignore that, and MSFT is happy + * to help them do so. So now people expect to use + * such nonconformant devices with Linux too; sigh. + */ + info1 |= max_packet(maxp) << 16; + info2 |= (FOTG210_TUNE_MULT_HS << 30); + } else { /* PIPE_INTERRUPT */ + info1 |= max_packet(maxp) << 16; + info2 |= hb_mult(maxp) << 30; + } + break; + default: + fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev, + urb->dev->speed); +done: + qh_destroy(fotg210, qh); + return NULL; + } + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ + + /* init as live, toggle clear, advance to dummy */ + qh->qh_state = QH_STATE_IDLE; + hw = qh->hw; + hw->hw_info1 = cpu_to_hc32(fotg210, info1); + hw->hw_info2 = cpu_to_hc32(fotg210, info2); + qh->is_out = !is_input; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); + qh_refresh(fotg210, qh); + return qh; +} + +/*-------------------------------------------------------------------------*/ + +static void enable_async(struct fotg210_hcd *fotg210) +{ + if (fotg210->async_count++) + return; + + /* Stop waiting to turn off the async schedule */ + fotg210->enabled_hrtimer_events &= ~BIT(FOTG210_HRTIMER_DISABLE_ASYNC); + + /* Don't start the schedule until ASS is 0 */ + fotg210_poll_ASS(fotg210); + turn_on_io_watchdog(fotg210); +} + +static void disable_async(struct fotg210_hcd *fotg210) +{ + if (--fotg210->async_count) + return; + + /* The async schedule and async_unlink list are supposed to be empty */ + WARN_ON(fotg210->async->qh_next.qh || fotg210->async_unlink); + + /* Don't turn off the schedule until ASS is 1 */ + fotg210_poll_ASS(fotg210); +} + +/* move qh (and its qtds) onto async queue; maybe enable queue. */ + +static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); + struct fotg210_qh *head; + + /* Don't link a QH if there's a Clear-TT-Buffer pending */ + if (unlikely(qh->clearing_tt)) + return; + + WARN_ON(qh->qh_state != QH_STATE_IDLE); + + /* clear halt and/or toggle; and maybe recover from silicon quirk */ + qh_refresh(fotg210, qh); + + /* splice right after start */ + head = fotg210->async; + qh->qh_next = head->qh_next; + qh->hw->hw_next = head->hw->hw_next; + wmb(); + + head->qh_next.qh = qh; + head->hw->hw_next = dma; + + qh->xacterrs = 0; + qh->qh_state = QH_STATE_LINKED; + /* qtd completions reported later by interrupt */ + + enable_async(fotg210); +} + +/*-------------------------------------------------------------------------*/ + +/* + * For control/bulk/interrupt, return QH with these TDs appended. + * Allocates and initializes the QH if necessary. + * Returns null if it can't allocate a QH it needs to. + * If the QH has TDs (urbs) already, that's great. + */ +static struct fotg210_qh *qh_append_tds( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *qtd_list, + int epnum, + void **ptr +) +{ + struct fotg210_qh *qh = NULL; + __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); + + qh = (struct fotg210_qh *) *ptr; + if (unlikely(qh == NULL)) { + /* can't sleep here, we have fotg210->lock... */ + qh = qh_make(fotg210, urb, GFP_ATOMIC); + *ptr = qh; + } + if (likely(qh != NULL)) { + struct fotg210_qtd *qtd; + + if (unlikely(list_empty(qtd_list))) + qtd = NULL; + else + qtd = list_entry(qtd_list->next, struct fotg210_qtd, + qtd_list); + + /* control qh may need patching ... */ + if (unlikely(epnum == 0)) { + /* usb_reset_device() briefly reverts to address 0 */ + if (usb_pipedevice(urb->pipe) == 0) + qh->hw->hw_info1 &= ~qh_addr_mask; + } + + /* just one way to queue requests: swap with the dummy qtd. + * only hc or qh_refresh() ever modify the overlay. + */ + if (likely(qtd != NULL)) { + struct fotg210_qtd *dummy; + dma_addr_t dma; + __hc32 token; + + /* to avoid racing the HC, use the dummy td instead of + * the first td of our list (becomes new dummy). both + * tds stay deactivated until we're done, when the + * HC is allowed to fetch the old dummy (4.10.2). + */ + token = qtd->hw_token; + qtd->hw_token = HALT_BIT(fotg210); + + dummy = qh->dummy; + + dma = dummy->qtd_dma; + *dummy = *qtd; + dummy->qtd_dma = dma; + + list_del(&qtd->qtd_list); + list_add(&dummy->qtd_list, qtd_list); + list_splice_tail(qtd_list, &qh->qtd_list); + + fotg210_qtd_init(fotg210, qtd, qtd->qtd_dma); + qh->dummy = qtd; + + /* hc must see the new dummy at list end */ + dma = qtd->qtd_dma; + qtd = list_entry(qh->qtd_list.prev, + struct fotg210_qtd, qtd_list); + qtd->hw_next = QTD_NEXT(fotg210, dma); + + /* let the hc process these next qtds */ + wmb(); + dummy->hw_token = token; + + urb->hcpriv = qh; + } + } + return qh; +} + +/*-------------------------------------------------------------------------*/ + +static int +submit_async( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *qtd_list, + gfp_t mem_flags +) { + int epnum; + unsigned long flags; + struct fotg210_qh *qh = NULL; + int rc; + + epnum = urb->ep->desc.bEndpointAddress; + +#ifdef FOTG210_URB_TRACE + { + struct fotg210_qtd *qtd; + qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list); + fotg210_dbg(fotg210, + "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", + __func__, urb->dev->devpath, urb, + epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", + urb->transfer_buffer_length, + qtd, urb->ep->hcpriv); + } +#endif + + spin_lock_irqsave(&fotg210->lock, flags); + if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { + rc = -ESHUTDOWN; + goto done; + } + rc = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); + if (unlikely(rc)) + goto done; + + qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); + if (unlikely(qh == NULL)) { + usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); + rc = -ENOMEM; + goto done; + } + + /* Control/bulk operations through TTs don't need scheduling, + * the HC and TT handle it when the TT has a buffer ready. + */ + if (likely(qh->qh_state == QH_STATE_IDLE)) + qh_link_async(fotg210, qh); + done: + spin_unlock_irqrestore(&fotg210->lock, flags); + if (unlikely(qh == NULL)) + qtd_list_free(fotg210, urb, qtd_list); + return rc; +} + +/*-------------------------------------------------------------------------*/ + +static void single_unlink_async(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh) +{ + struct fotg210_qh *prev; + + /* Add to the end of the list of QHs waiting for the next IAAD */ + qh->qh_state = QH_STATE_UNLINK; + if (fotg210->async_unlink) + fotg210->async_unlink_last->unlink_next = qh; + else + fotg210->async_unlink = qh; + fotg210->async_unlink_last = qh; + + /* Unlink it from the schedule */ + prev = fotg210->async; + while (prev->qh_next.qh != qh) + prev = prev->qh_next.qh; + + prev->hw->hw_next = qh->hw->hw_next; + prev->qh_next = qh->qh_next; + if (fotg210->qh_scan_next == qh) + fotg210->qh_scan_next = qh->qh_next.qh; +} + +static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) +{ + /* + * Do nothing if an IAA cycle is already running or + * if one will be started shortly. + */ + if (fotg210->async_iaa || fotg210->async_unlinking) + return; + + /* Do all the waiting QHs at once */ + fotg210->async_iaa = fotg210->async_unlink; + fotg210->async_unlink = NULL; + + /* If the controller isn't running, we don't have to wait for it */ + if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) { + if (!nested) /* Avoid recursion */ + end_unlink_async(fotg210); + + /* Otherwise start a new IAA cycle */ + } else if (likely(fotg210->rh_state == FOTG210_RH_RUNNING)) { + /* Make sure the unlinks are all visible to the hardware */ + wmb(); + + fotg210_writel(fotg210, fotg210->command | CMD_IAAD, + &fotg210->regs->command); + fotg210_readl(fotg210, &fotg210->regs->command); + fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG, + true); + } +} + +/* the async qh for the qtds being unlinked are now gone from the HC */ + +static void end_unlink_async(struct fotg210_hcd *fotg210) +{ + struct fotg210_qh *qh; + + /* Process the idle QHs */ + restart: + fotg210->async_unlinking = true; + while (fotg210->async_iaa) { + qh = fotg210->async_iaa; + fotg210->async_iaa = qh->unlink_next; + qh->unlink_next = NULL; + + qh->qh_state = QH_STATE_IDLE; + qh->qh_next.qh = NULL; + + qh_completions(fotg210, qh); + if (!list_empty(&qh->qtd_list) && + fotg210->rh_state == FOTG210_RH_RUNNING) + qh_link_async(fotg210, qh); + disable_async(fotg210); + } + fotg210->async_unlinking = false; + + /* Start a new IAA cycle if any QHs are waiting for it */ + if (fotg210->async_unlink) { + start_iaa_cycle(fotg210, true); + if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) + goto restart; + } +} + +static void unlink_empty_async(struct fotg210_hcd *fotg210) +{ + struct fotg210_qh *qh, *next; + bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); + bool check_unlinks_later = false; + + /* Unlink all the async QHs that have been empty for a timer cycle */ + next = fotg210->async->qh_next.qh; + while (next) { + qh = next; + next = qh->qh_next.qh; + + if (list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED) { + if (!stopped && qh->unlink_cycle == + fotg210->async_unlink_cycle) + check_unlinks_later = true; + else + single_unlink_async(fotg210, qh); + } + } + + /* Start a new IAA cycle if any QHs are waiting for it */ + if (fotg210->async_unlink) + start_iaa_cycle(fotg210, false); + + /* QHs that haven't been empty for long enough will be handled later */ + if (check_unlinks_later) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS, + true); + ++fotg210->async_unlink_cycle; + } +} + +/* makes sure the async qh will become idle */ +/* caller must own fotg210->lock */ + +static void start_unlink_async(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh) +{ + /* + * If the QH isn't linked then there's nothing we can do + * unless we were called during a giveback, in which case + * qh_completions() has to deal with it. + */ + if (qh->qh_state != QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_COMPLETING) + qh->needs_rescan = 1; + return; + } + + single_unlink_async(fotg210, qh); + start_iaa_cycle(fotg210, false); +} + +/*-------------------------------------------------------------------------*/ + +static void scan_async(struct fotg210_hcd *fotg210) +{ + struct fotg210_qh *qh; + bool check_unlinks_later = false; + + fotg210->qh_scan_next = fotg210->async->qh_next.qh; + while (fotg210->qh_scan_next) { + qh = fotg210->qh_scan_next; + fotg210->qh_scan_next = qh->qh_next.qh; + rescan: + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list)) { + int temp; + + /* + * Unlinks could happen here; completion reporting + * drops the lock. That's why fotg210->qh_scan_next + * always holds the next qh to scan; if the next qh + * gets unlinked then fotg210->qh_scan_next is adjusted + * in single_unlink_async(). + */ + temp = qh_completions(fotg210, qh); + if (qh->needs_rescan) { + start_unlink_async(fotg210, qh); + } else if (list_empty(&qh->qtd_list) + && qh->qh_state == QH_STATE_LINKED) { + qh->unlink_cycle = fotg210->async_unlink_cycle; + check_unlinks_later = true; + } else if (temp != 0) + goto rescan; + } + } + + /* + * Unlink empty entries, reducing DMA usage as well + * as HCD schedule-scanning costs. Delay for any qh + * we just scanned, there's a not-unusual case that it + * doesn't stay idle for long. + */ + if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING && + !(fotg210->enabled_hrtimer_events & + BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { + fotg210_enable_event(fotg210, + FOTG210_HRTIMER_ASYNC_UNLINKS, true); + ++fotg210->async_unlink_cycle; + } +} +/*-------------------------------------------------------------------------*/ +/* + * EHCI scheduled transaction support: interrupt, iso, split iso + * These are called "periodic" transactions in the EHCI spec. + * + * Note that for interrupt transfers, the QH/QTD manipulation is shared + * with the "asynchronous" transaction support (control/bulk transfers). + * The only real difference is in how interrupt transfers are scheduled. + * + * For ISO, we make an "iso_stream" head to serve the same role as a QH. + * It keeps track of every ITD (or SITD) that's linked, and holds enough + * pre-calculated schedule data to make appending to the queue be quick. + */ + +static int fotg210_get_frame(struct usb_hcd *hcd); + +/*-------------------------------------------------------------------------*/ + +/* + * periodic_next_shadow - return "next" pointer on shadow list + * @periodic: host pointer to qh/itd + * @tag: hardware tag for type of this record + */ +static union fotg210_shadow * +periodic_next_shadow(struct fotg210_hcd *fotg210, + union fotg210_shadow *periodic, __hc32 tag) +{ + switch (hc32_to_cpu(fotg210, tag)) { + case Q_TYPE_QH: + return &periodic->qh->qh_next; + case Q_TYPE_FSTN: + return &periodic->fstn->fstn_next; + default: + return &periodic->itd->itd_next; + } +} + +static __hc32 * +shadow_next_periodic(struct fotg210_hcd *fotg210, + union fotg210_shadow *periodic, __hc32 tag) +{ + switch (hc32_to_cpu(fotg210, tag)) { + /* our fotg210_shadow.qh is actually software part */ + case Q_TYPE_QH: + return &periodic->qh->hw->hw_next; + /* others are hw parts */ + default: + return periodic->hw_next; + } +} + +/* caller must hold fotg210->lock */ +static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, + void *ptr) +{ + union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; + __hc32 *hw_p = &fotg210->periodic[frame]; + union fotg210_shadow here = *prev_p; + + /* find predecessor of "ptr"; hw and shadow lists are in sync */ + while (here.ptr && here.ptr != ptr) { + prev_p = periodic_next_shadow(fotg210, prev_p, + Q_NEXT_TYPE(fotg210, *hw_p)); + hw_p = shadow_next_periodic(fotg210, &here, + Q_NEXT_TYPE(fotg210, *hw_p)); + here = *prev_p; + } + /* an interrupt entry (at list end) could have been shared */ + if (!here.ptr) + return; + + /* update shadow and hardware lists ... the old "next" pointers + * from ptr may still be in use, the caller updates them. + */ + *prev_p = *periodic_next_shadow(fotg210, &here, + Q_NEXT_TYPE(fotg210, *hw_p)); + + *hw_p = *shadow_next_periodic(fotg210, &here, + Q_NEXT_TYPE(fotg210, *hw_p)); +} + +/* how many of the uframe's 125 usecs are allocated? */ +static unsigned short +periodic_usecs(struct fotg210_hcd *fotg210, unsigned frame, unsigned uframe) +{ + __hc32 *hw_p = &fotg210->periodic[frame]; + union fotg210_shadow *q = &fotg210->pshadow[frame]; + unsigned usecs = 0; + struct fotg210_qh_hw *hw; + + while (q->ptr) { + switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) { + case Q_TYPE_QH: + hw = q->qh->hw; + /* is it in the S-mask? */ + if (hw->hw_info2 & cpu_to_hc32(fotg210, 1 << uframe)) + usecs += q->qh->usecs; + /* ... or C-mask? */ + if (hw->hw_info2 & cpu_to_hc32(fotg210, + 1 << (8 + uframe))) + usecs += q->qh->c_usecs; + hw_p = &hw->hw_next; + q = &q->qh->qh_next; + break; + /* case Q_TYPE_FSTN: */ + default: + /* for "save place" FSTNs, count the relevant INTR + * bandwidth from the previous frame + */ + if (q->fstn->hw_prev != FOTG210_LIST_END(fotg210)) + fotg210_dbg(fotg210, "ignoring FSTN cost ...\n"); + + hw_p = &q->fstn->hw_next; + q = &q->fstn->fstn_next; + break; + case Q_TYPE_ITD: + if (q->itd->hw_transaction[uframe]) + usecs += q->itd->stream->usecs; + hw_p = &q->itd->hw_next; + q = &q->itd->itd_next; + break; + } + } +#ifdef DEBUG + if (usecs > fotg210->uframe_periodic_max) + fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", + frame * 8 + uframe, usecs); +#endif + return usecs; +} + +/*-------------------------------------------------------------------------*/ + +static int same_tt(struct usb_device *dev1, struct usb_device *dev2) +{ + if (!dev1->tt || !dev2->tt) + return 0; + if (dev1->tt != dev2->tt) + return 0; + if (dev1->tt->multi) + return dev1->ttport == dev2->ttport; + else + return 1; +} + +/* return true iff the device's transaction translator is available + * for a periodic transfer starting at the specified frame, using + * all the uframes in the mask. + */ +static int tt_no_collision( + struct fotg210_hcd *fotg210, + unsigned period, + struct usb_device *dev, + unsigned frame, + u32 uf_mask +) +{ + if (period == 0) /* error */ + return 0; + + /* note bandwidth wastage: split never follows csplit + * (different dev or endpoint) until the next uframe. + * calling convention doesn't make that distinction. + */ + for (; frame < fotg210->periodic_size; frame += period) { + union fotg210_shadow here; + __hc32 type; + struct fotg210_qh_hw *hw; + + here = fotg210->pshadow[frame]; + type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]); + while (here.ptr) { + switch (hc32_to_cpu(fotg210, type)) { + case Q_TYPE_ITD: + type = Q_NEXT_TYPE(fotg210, here.itd->hw_next); + here = here.itd->itd_next; + continue; + case Q_TYPE_QH: + hw = here.qh->hw; + if (same_tt(dev, here.qh->dev)) { + u32 mask; + + mask = hc32_to_cpu(fotg210, + hw->hw_info2); + /* "knows" no gap is needed */ + mask |= mask >> 8; + if (mask & uf_mask) + break; + } + type = Q_NEXT_TYPE(fotg210, hw->hw_next); + here = here.qh->qh_next; + continue; + /* case Q_TYPE_FSTN: */ + default: + fotg210_dbg(fotg210, + "periodic frame %d bogus type %d\n", + frame, type); + } + + /* collision or error */ + return 0; + } + } + + /* no collision */ + return 1; +} + +/*-------------------------------------------------------------------------*/ + +static void enable_periodic(struct fotg210_hcd *fotg210) +{ + if (fotg210->periodic_count++) + return; + + /* Stop waiting to turn off the periodic schedule */ + fotg210->enabled_hrtimer_events &= + ~BIT(FOTG210_HRTIMER_DISABLE_PERIODIC); + + /* Don't start the schedule until PSS is 0 */ + fotg210_poll_PSS(fotg210); + turn_on_io_watchdog(fotg210); +} + +static void disable_periodic(struct fotg210_hcd *fotg210) +{ + if (--fotg210->periodic_count) + return; + + /* Don't turn off the schedule until PSS is 1 */ + fotg210_poll_PSS(fotg210); +} + +/*-------------------------------------------------------------------------*/ + +/* periodic schedule slots have iso tds (normal or split) first, then a + * sparse tree for active interrupt transfers. + * + * this just links in a qh; caller guarantees uframe masks are set right. + * no FSTN support (yet; fotg210 0.96+) + */ +static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + unsigned i; + unsigned period = qh->period; + + dev_dbg(&qh->dev->dev, + "link qh%d-%04x/%p start %d [%d/%d us]\n", + period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) + & (QH_CMASK | QH_SMASK), + qh, qh->start, qh->usecs, qh->c_usecs); + + /* high bandwidth, or otherwise every microframe */ + if (period == 0) + period = 1; + + for (i = qh->start; i < fotg210->periodic_size; i += period) { + union fotg210_shadow *prev = &fotg210->pshadow[i]; + __hc32 *hw_p = &fotg210->periodic[i]; + union fotg210_shadow here = *prev; + __hc32 type = 0; + + /* skip the iso nodes at list head */ + while (here.ptr) { + type = Q_NEXT_TYPE(fotg210, *hw_p); + if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) + break; + prev = periodic_next_shadow(fotg210, prev, type); + hw_p = shadow_next_periodic(fotg210, &here, type); + here = *prev; + } + + /* sorting each branch by period (slow-->fast) + * enables sharing interior tree nodes + */ + while (here.ptr && qh != here.qh) { + if (qh->period > here.qh->period) + break; + prev = &here.qh->qh_next; + hw_p = &here.qh->hw->hw_next; + here = *prev; + } + /* link in this qh, unless some earlier pass did that */ + if (qh != here.qh) { + qh->qh_next = here; + if (here.qh) + qh->hw->hw_next = *hw_p; + wmb(); + prev->qh = qh; + *hw_p = QH_NEXT(fotg210, qh->qh_dma); + } + } + qh->qh_state = QH_STATE_LINKED; + qh->xacterrs = 0; + + /* update per-qh bandwidth for usbfs */ + fotg210_to_hcd(fotg210)->self.bandwidth_allocated += qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + list_add(&qh->intr_node, &fotg210->intr_qh_list); + + /* maybe enable periodic schedule processing */ + ++fotg210->intr_count; + enable_periodic(fotg210); +} + +static void qh_unlink_periodic(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh) +{ + unsigned i; + unsigned period; + + /* + * If qh is for a low/full-speed device, simply unlinking it + * could interfere with an ongoing split transaction. To unlink + * it safely would require setting the QH_INACTIVATE bit and + * waiting at least one frame, as described in EHCI 4.12.2.5. + * + * We won't bother with any of this. Instead, we assume that the + * only reason for unlinking an interrupt QH while the current URB + * is still active is to dequeue all the URBs (flush the whole + * endpoint queue). + * + * If rebalancing the periodic schedule is ever implemented, this + * approach will no longer be valid. + */ + + /* high bandwidth, or otherwise part of every microframe */ + period = qh->period; + if (!period) + period = 1; + + for (i = qh->start; i < fotg210->periodic_size; i += period) + periodic_unlink(fotg210, i, qh); + + /* update per-qh bandwidth for usbfs */ + fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + dev_dbg(&qh->dev->dev, + "unlink qh%d-%04x/%p start %d [%d/%d us]\n", + qh->period, + hc32_to_cpup(fotg210, &qh->hw->hw_info2) & + (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); + + /* qh->qh_next still "live" to HC */ + qh->qh_state = QH_STATE_UNLINK; + qh->qh_next.ptr = NULL; + + if (fotg210->qh_scan_next == qh) + fotg210->qh_scan_next = list_entry(qh->intr_node.next, + struct fotg210_qh, intr_node); + list_del(&qh->intr_node); +} + +static void start_unlink_intr(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh) +{ + /* If the QH isn't linked then there's nothing we can do + * unless we were called during a giveback, in which case + * qh_completions() has to deal with it. + */ + if (qh->qh_state != QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_COMPLETING) + qh->needs_rescan = 1; + return; + } + + qh_unlink_periodic(fotg210, qh); + + /* Make sure the unlinks are visible before starting the timer */ + wmb(); + + /* + * The EHCI spec doesn't say how long it takes the controller to + * stop accessing an unlinked interrupt QH. The timer delay is + * 9 uframes; presumably that will be long enough. + */ + qh->unlink_cycle = fotg210->intr_unlink_cycle; + + /* New entries go at the end of the intr_unlink list */ + if (fotg210->intr_unlink) + fotg210->intr_unlink_last->unlink_next = qh; + else + fotg210->intr_unlink = qh; + fotg210->intr_unlink_last = qh; + + if (fotg210->intr_unlinking) + ; /* Avoid recursive calls */ + else if (fotg210->rh_state < FOTG210_RH_RUNNING) + fotg210_handle_intr_unlinks(fotg210); + else if (fotg210->intr_unlink == qh) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, + true); + ++fotg210->intr_unlink_cycle; + } +} + +static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + struct fotg210_qh_hw *hw = qh->hw; + int rc; + + qh->qh_state = QH_STATE_IDLE; + hw->hw_next = FOTG210_LIST_END(fotg210); + + qh_completions(fotg210, qh); + + /* reschedule QH iff another request is queued */ + if (!list_empty(&qh->qtd_list) && + fotg210->rh_state == FOTG210_RH_RUNNING) { + rc = qh_schedule(fotg210, qh); + + /* An error here likely indicates handshake failure + * or no space left in the schedule. Neither fault + * should happen often ... + * + * FIXME kill the now-dysfunctional queued urbs + */ + if (rc != 0) + fotg210_err(fotg210, "can't reschedule qh %p, err %d\n", + qh, rc); + } + + /* maybe turn off periodic schedule */ + --fotg210->intr_count; + disable_periodic(fotg210); +} + +/*-------------------------------------------------------------------------*/ + +static int check_period( + struct fotg210_hcd *fotg210, + unsigned frame, + unsigned uframe, + unsigned period, + unsigned usecs +) { + int claimed; + + /* complete split running into next frame? + * given FSTN support, we could sometimes check... + */ + if (uframe >= 8) + return 0; + + /* convert "usecs we need" to "max already claimed" */ + usecs = fotg210->uframe_periodic_max - usecs; + + /* we "know" 2 and 4 uframe intervals were rejected; so + * for period 0, check _every_ microframe in the schedule. + */ + if (unlikely(period == 0)) { + do { + for (uframe = 0; uframe < 7; uframe++) { + claimed = periodic_usecs(fotg210, frame, + uframe); + if (claimed > usecs) + return 0; + } + } while ((frame += 1) < fotg210->periodic_size); + + /* just check the specified uframe, at that period */ + } else { + do { + claimed = periodic_usecs(fotg210, frame, uframe); + if (claimed > usecs) + return 0; + } while ((frame += period) < fotg210->periodic_size); + } + + /* success! */ + return 1; +} + +static int check_intr_schedule( + struct fotg210_hcd *fotg210, + unsigned frame, + unsigned uframe, + const struct fotg210_qh *qh, + __hc32 *c_maskp +) +{ + int retval = -ENOSPC; + u8 mask = 0; + + if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ + goto done; + + if (!check_period(fotg210, frame, uframe, qh->period, qh->usecs)) + goto done; + if (!qh->c_usecs) { + retval = 0; + *c_maskp = 0; + goto done; + } + + /* Make sure this tt's buffer is also available for CSPLITs. + * We pessimize a bit; probably the typical full speed case + * doesn't need the second CSPLIT. + * + * NOTE: both SPLIT and CSPLIT could be checked in just + * one smart pass... + */ + mask = 0x03 << (uframe + qh->gap_uf); + *c_maskp = cpu_to_hc32(fotg210, mask << 8); + + mask |= 1 << uframe; + if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) { + if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1, + qh->period, qh->c_usecs)) + goto done; + if (!check_period(fotg210, frame, uframe + qh->gap_uf, + qh->period, qh->c_usecs)) + goto done; + retval = 0; + } +done: + return retval; +} + +/* "first fit" scheduling policy used the first time through, + * or when the previous schedule slot can't be re-used. + */ +static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + int status; + unsigned uframe; + __hc32 c_mask; + unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + struct fotg210_qh_hw *hw = qh->hw; + + qh_refresh(fotg210, qh); + hw->hw_next = FOTG210_LIST_END(fotg210); + frame = qh->start; + + /* reuse the previous schedule slots, if we can */ + if (frame < qh->period) { + uframe = ffs(hc32_to_cpup(fotg210, &hw->hw_info2) & QH_SMASK); + status = check_intr_schedule(fotg210, frame, --uframe, + qh, &c_mask); + } else { + uframe = 0; + c_mask = 0; + status = -ENOSPC; + } + + /* else scan the schedule to find a group of slots such that all + * uframes have enough periodic bandwidth available. + */ + if (status) { + /* "normal" case, uframing flexible except with splits */ + if (qh->period) { + int i; + + for (i = qh->period; status && i > 0; --i) { + frame = ++fotg210->random_frame % qh->period; + for (uframe = 0; uframe < 8; uframe++) { + status = check_intr_schedule(fotg210, + frame, uframe, qh, + &c_mask); + if (status == 0) + break; + } + } + + /* qh->period == 0 means every uframe */ + } else { + frame = 0; + status = check_intr_schedule(fotg210, 0, 0, qh, + &c_mask); + } + if (status) + goto done; + qh->start = frame; + + /* reset S-frame and (maybe) C-frame masks */ + hw->hw_info2 &= cpu_to_hc32(fotg210, ~(QH_CMASK | QH_SMASK)); + hw->hw_info2 |= qh->period + ? cpu_to_hc32(fotg210, 1 << uframe) + : cpu_to_hc32(fotg210, QH_SMASK); + hw->hw_info2 |= c_mask; + } else + fotg210_dbg(fotg210, "reused qh %p schedule\n", qh); + + /* stuff into the periodic schedule */ + qh_link_periodic(fotg210, qh); +done: + return status; +} + +static int intr_submit( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *qtd_list, + gfp_t mem_flags +) { + unsigned epnum; + unsigned long flags; + struct fotg210_qh *qh; + int status; + struct list_head empty; + + /* get endpoint and transfer/schedule data */ + epnum = urb->ep->desc.bEndpointAddress; + + spin_lock_irqsave(&fotg210->lock, flags); + + if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { + status = -ESHUTDOWN; + goto done_not_linked; + } + status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); + if (unlikely(status)) + goto done_not_linked; + + /* get qh and force any scheduling errors */ + INIT_LIST_HEAD(&empty); + qh = qh_append_tds(fotg210, urb, &empty, epnum, &urb->ep->hcpriv); + if (qh == NULL) { + status = -ENOMEM; + goto done; + } + if (qh->qh_state == QH_STATE_IDLE) { + status = qh_schedule(fotg210, qh); + if (status) + goto done; + } + + /* then queue the urb's tds to the qh */ + qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); + BUG_ON(qh == NULL); + + /* ... update usbfs periodic stats */ + fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs++; + +done: + if (unlikely(status)) + usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); +done_not_linked: + spin_unlock_irqrestore(&fotg210->lock, flags); + if (status) + qtd_list_free(fotg210, urb, qtd_list); + + return status; +} + +static void scan_intr(struct fotg210_hcd *fotg210) +{ + struct fotg210_qh *qh; + + list_for_each_entry_safe(qh, fotg210->qh_scan_next, + &fotg210->intr_qh_list, intr_node) { + rescan: + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list)) { + int temp; + + /* + * Unlinks could happen here; completion reporting + * drops the lock. That's why fotg210->qh_scan_next + * always holds the next qh to scan; if the next qh + * gets unlinked then fotg210->qh_scan_next is adjusted + * in qh_unlink_periodic(). + */ + temp = qh_completions(fotg210, qh); + if (unlikely(qh->needs_rescan || + (list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED))) + start_unlink_intr(fotg210, qh); + else if (temp != 0) + goto rescan; + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* fotg210_iso_stream ops work with both ITD and SITD */ + +static struct fotg210_iso_stream * +iso_stream_alloc(gfp_t mem_flags) +{ + struct fotg210_iso_stream *stream; + + stream = kzalloc(sizeof(*stream), mem_flags); + if (likely(stream != NULL)) { + INIT_LIST_HEAD(&stream->td_list); + INIT_LIST_HEAD(&stream->free_list); + stream->next_uframe = -1; + } + return stream; +} + +static void +iso_stream_init( + struct fotg210_hcd *fotg210, + struct fotg210_iso_stream *stream, + struct usb_device *dev, + int pipe, + unsigned interval +) +{ + u32 buf1; + unsigned epnum, maxp; + int is_input; + long bandwidth; + unsigned multi; + + /* + * this might be a "high bandwidth" highspeed endpoint, + * as encoded in the ep descriptor's wMaxPacket field + */ + epnum = usb_pipeendpoint(pipe); + is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; + maxp = usb_maxpacket(dev, pipe, !is_input); + if (is_input) + buf1 = (1 << 11); + else + buf1 = 0; + + maxp = max_packet(maxp); + multi = hb_mult(maxp); + buf1 |= maxp; + maxp *= multi; + + stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); + stream->buf1 = cpu_to_hc32(fotg210, buf1); + stream->buf2 = cpu_to_hc32(fotg210, multi); + + /* usbfs wants to report the average usecs per frame tied up + * when transfers on this endpoint are scheduled ... + */ + if (dev->speed == USB_SPEED_FULL) { + interval <<= 3; + stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, + is_input, 1, maxp)); + stream->usecs /= 8; + } else { + stream->highspeed = 1; + stream->usecs = HS_USECS_ISO(maxp); + } + bandwidth = stream->usecs * 8; + bandwidth /= interval; + + stream->bandwidth = bandwidth; + stream->udev = dev; + stream->bEndpointAddress = is_input | epnum; + stream->interval = interval; + stream->maxp = maxp; +} + +static struct fotg210_iso_stream * +iso_stream_find(struct fotg210_hcd *fotg210, struct urb *urb) +{ + unsigned epnum; + struct fotg210_iso_stream *stream; + struct usb_host_endpoint *ep; + unsigned long flags; + + epnum = usb_pipeendpoint(urb->pipe); + if (usb_pipein(urb->pipe)) + ep = urb->dev->ep_in[epnum]; + else + ep = urb->dev->ep_out[epnum]; + + spin_lock_irqsave(&fotg210->lock, flags); + stream = ep->hcpriv; + + if (unlikely(stream == NULL)) { + stream = iso_stream_alloc(GFP_ATOMIC); + if (likely(stream != NULL)) { + ep->hcpriv = stream; + stream->ep = ep; + iso_stream_init(fotg210, stream, urb->dev, urb->pipe, + urb->interval); + } + + /* if dev->ep[epnum] is a QH, hw is set */ + } else if (unlikely(stream->hw != NULL)) { + fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n", + urb->dev->devpath, epnum, + usb_pipein(urb->pipe) ? "in" : "out"); + stream = NULL; + } + + spin_unlock_irqrestore(&fotg210->lock, flags); + return stream; +} + +/*-------------------------------------------------------------------------*/ + +/* fotg210_iso_sched ops can be ITD-only or SITD-only */ + +static struct fotg210_iso_sched * +iso_sched_alloc(unsigned packets, gfp_t mem_flags) +{ + struct fotg210_iso_sched *iso_sched; + int size = sizeof(*iso_sched); + + size += packets * sizeof(struct fotg210_iso_packet); + iso_sched = kzalloc(size, mem_flags); + if (likely(iso_sched != NULL)) + INIT_LIST_HEAD(&iso_sched->td_list); + + return iso_sched; +} + +static inline void +itd_sched_init( + struct fotg210_hcd *fotg210, + struct fotg210_iso_sched *iso_sched, + struct fotg210_iso_stream *stream, + struct urb *urb +) +{ + unsigned i; + dma_addr_t dma = urb->transfer_dma; + + /* how many uframes are needed for these transfers */ + iso_sched->span = urb->number_of_packets * stream->interval; + + /* figure out per-uframe itd fields that we'll need later + * when we fit new itds into the schedule. + */ + for (i = 0; i < urb->number_of_packets; i++) { + struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; + unsigned length; + dma_addr_t buf; + u32 trans; + + length = urb->iso_frame_desc[i].length; + buf = dma + urb->iso_frame_desc[i].offset; + + trans = FOTG210_ISOC_ACTIVE; + trans |= buf & 0x0fff; + if (unlikely(((i + 1) == urb->number_of_packets)) + && !(urb->transfer_flags & URB_NO_INTERRUPT)) + trans |= FOTG210_ITD_IOC; + trans |= length << 16; + uframe->transaction = cpu_to_hc32(fotg210, trans); + + /* might need to cross a buffer page within a uframe */ + uframe->bufp = (buf & ~(u64)0x0fff); + buf += length; + if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff)))) + uframe->cross = 1; + } +} + +static void +iso_sched_free( + struct fotg210_iso_stream *stream, + struct fotg210_iso_sched *iso_sched +) +{ + if (!iso_sched) + return; + /* caller must hold fotg210->lock!*/ + list_splice(&iso_sched->td_list, &stream->free_list); + kfree(iso_sched); +} + +static int +itd_urb_transaction( + struct fotg210_iso_stream *stream, + struct fotg210_hcd *fotg210, + struct urb *urb, + gfp_t mem_flags +) +{ + struct fotg210_itd *itd; + dma_addr_t itd_dma; + int i; + unsigned num_itds; + struct fotg210_iso_sched *sched; + unsigned long flags; + + sched = iso_sched_alloc(urb->number_of_packets, mem_flags); + if (unlikely(sched == NULL)) + return -ENOMEM; + + itd_sched_init(fotg210, sched, stream, urb); + + if (urb->interval < 8) + num_itds = 1 + (sched->span + 7) / 8; + else + num_itds = urb->number_of_packets; + + /* allocate/init ITDs */ + spin_lock_irqsave(&fotg210->lock, flags); + for (i = 0; i < num_itds; i++) { + + /* + * Use iTDs from the free list, but not iTDs that may + * still be in use by the hardware. + */ + if (likely(!list_empty(&stream->free_list))) { + itd = list_first_entry(&stream->free_list, + struct fotg210_itd, itd_list); + if (itd->frame == fotg210->now_frame) + goto alloc_itd; + list_del(&itd->itd_list); + itd_dma = itd->itd_dma; + } else { + alloc_itd: + spin_unlock_irqrestore(&fotg210->lock, flags); + itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, + &itd_dma); + spin_lock_irqsave(&fotg210->lock, flags); + if (!itd) { + iso_sched_free(stream, sched); + spin_unlock_irqrestore(&fotg210->lock, flags); + return -ENOMEM; + } + } + + memset(itd, 0, sizeof(*itd)); + itd->itd_dma = itd_dma; + list_add(&itd->itd_list, &sched->td_list); + } + spin_unlock_irqrestore(&fotg210->lock, flags); + + /* temporarily store schedule info in hcpriv */ + urb->hcpriv = sched; + urb->error_count = 0; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static inline int +itd_slot_ok( + struct fotg210_hcd *fotg210, + u32 mod, + u32 uframe, + u8 usecs, + u32 period +) +{ + uframe %= period; + do { + /* can't commit more than uframe_periodic_max usec */ + if (periodic_usecs(fotg210, uframe >> 3, uframe & 0x7) + > (fotg210->uframe_periodic_max - usecs)) + return 0; + + /* we know urb->interval is 2^N uframes */ + uframe += period; + } while (uframe < mod); + return 1; +} + +/* + * This scheduler plans almost as far into the future as it has actual + * periodic schedule slots. (Affected by TUNE_FLS, which defaults to + * "as small as possible" to be cache-friendlier.) That limits the size + * transfers you can stream reliably; avoid more than 64 msec per urb. + * Also avoid queue depths of less than fotg210's worst irq latency (affected + * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter, + * and other factors); or more than about 230 msec total (for portability, + * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler! + */ + +#define SCHEDULE_SLOP 80 /* microframes */ + +static int +iso_stream_schedule( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct fotg210_iso_stream *stream +) +{ + u32 now, next, start, period, span; + int status; + unsigned mod = fotg210->periodic_size << 3; + struct fotg210_iso_sched *sched = urb->hcpriv; + + period = urb->interval; + span = sched->span; + + if (span > mod - SCHEDULE_SLOP) { + fotg210_dbg(fotg210, "iso request %p too long\n", urb); + status = -EFBIG; + goto fail; + } + + now = fotg210_read_frame_index(fotg210) & (mod - 1); + + /* Typical case: reuse current schedule, stream is still active. + * Hopefully there are no gaps from the host falling behind + * (irq delays etc), but if there are we'll take the next + * slot in the schedule, implicitly assuming URB_ISO_ASAP. + */ + if (likely(!list_empty(&stream->td_list))) { + u32 excess; + + /* For high speed devices, allow scheduling within the + * isochronous scheduling threshold. For full speed devices + * and Intel PCI-based controllers, don't (work around for + * Intel ICH9 bug). + */ + if (!stream->highspeed && fotg210->fs_i_thresh) + next = now + fotg210->i_thresh; + else + next = now; + + /* Fell behind (by up to twice the slop amount)? + * We decide based on the time of the last currently-scheduled + * slot, not the time of the next available slot. + */ + excess = (stream->next_uframe - period - next) & (mod - 1); + if (excess >= mod - 2 * SCHEDULE_SLOP) + start = next + excess - mod + period * + DIV_ROUND_UP(mod - excess, period); + else + start = next + excess + period; + if (start - now >= mod) { + fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", + urb, start - now - period, period, + mod); + status = -EFBIG; + goto fail; + } + } + + /* need to schedule; when's the next (u)frame we could start? + * this is bigger than fotg210->i_thresh allows; scheduling itself + * isn't free, the slop should handle reasonably slow cpus. it + * can also help high bandwidth if the dma and irq loads don't + * jump until after the queue is primed. + */ + else { + int done = 0; + start = SCHEDULE_SLOP + (now & ~0x07); + + /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ + + /* find a uframe slot with enough bandwidth. + * Early uframes are more precious because full-speed + * iso IN transfers can't use late uframes, + * and therefore they should be allocated last. + */ + next = start; + start += period; + do { + start--; + /* check schedule: enough space? */ + if (itd_slot_ok(fotg210, mod, start, + stream->usecs, period)) + done = 1; + } while (start > next && !done); + + /* no room in the schedule */ + if (!done) { + fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n", + urb, now, now + mod); + status = -ENOSPC; + goto fail; + } + } + + /* Tried to schedule too far into the future? */ + if (unlikely(start - now + span - period + >= mod - 2 * SCHEDULE_SLOP)) { + fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", + urb, start - now, span - period, + mod - 2 * SCHEDULE_SLOP); + status = -EFBIG; + goto fail; + } + + stream->next_uframe = start & (mod - 1); + + /* report high speed start in uframes; full speed, in frames */ + urb->start_frame = stream->next_uframe; + if (!stream->highspeed) + urb->start_frame >>= 3; + + /* Make sure scan_isoc() sees these */ + if (fotg210->isoc_count == 0) + fotg210->next_frame = now >> 3; + return 0; + + fail: + iso_sched_free(stream, sched); + urb->hcpriv = NULL; + return status; +} + +/*-------------------------------------------------------------------------*/ + +static inline void +itd_init(struct fotg210_hcd *fotg210, struct fotg210_iso_stream *stream, + struct fotg210_itd *itd) +{ + int i; + + /* it's been recently zeroed */ + itd->hw_next = FOTG210_LIST_END(fotg210); + itd->hw_bufp[0] = stream->buf0; + itd->hw_bufp[1] = stream->buf1; + itd->hw_bufp[2] = stream->buf2; + + for (i = 0; i < 8; i++) + itd->index[i] = -1; + + /* All other fields are filled when scheduling */ +} + +static inline void +itd_patch( + struct fotg210_hcd *fotg210, + struct fotg210_itd *itd, + struct fotg210_iso_sched *iso_sched, + unsigned index, + u16 uframe +) +{ + struct fotg210_iso_packet *uf = &iso_sched->packet[index]; + unsigned pg = itd->pg; + + uframe &= 0x07; + itd->index[uframe] = index; + + itd->hw_transaction[uframe] = uf->transaction; + itd->hw_transaction[uframe] |= cpu_to_hc32(fotg210, pg << 12); + itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, uf->bufp & ~(u32)0); + itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(uf->bufp >> 32)); + + /* iso_frame_desc[].offset must be strictly increasing */ + if (unlikely(uf->cross)) { + u64 bufp = uf->bufp + 4096; + + itd->pg = ++pg; + itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0); + itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(bufp >> 32)); + } +} + +static inline void +itd_link(struct fotg210_hcd *fotg210, unsigned frame, struct fotg210_itd *itd) +{ + union fotg210_shadow *prev = &fotg210->pshadow[frame]; + __hc32 *hw_p = &fotg210->periodic[frame]; + union fotg210_shadow here = *prev; + __hc32 type = 0; + + /* skip any iso nodes which might belong to previous microframes */ + while (here.ptr) { + type = Q_NEXT_TYPE(fotg210, *hw_p); + if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) + break; + prev = periodic_next_shadow(fotg210, prev, type); + hw_p = shadow_next_periodic(fotg210, &here, type); + here = *prev; + } + + itd->itd_next = here; + itd->hw_next = *hw_p; + prev->itd = itd; + itd->frame = frame; + wmb(); + *hw_p = cpu_to_hc32(fotg210, itd->itd_dma | Q_TYPE_ITD); +} + +/* fit urb's itds into the selected schedule slot; activate as needed */ +static void itd_link_urb( + struct fotg210_hcd *fotg210, + struct urb *urb, + unsigned mod, + struct fotg210_iso_stream *stream +) +{ + int packet; + unsigned next_uframe, uframe, frame; + struct fotg210_iso_sched *iso_sched = urb->hcpriv; + struct fotg210_itd *itd; + + next_uframe = stream->next_uframe & (mod - 1); + + if (unlikely(list_empty(&stream->td_list))) { + fotg210_to_hcd(fotg210)->self.bandwidth_allocated + += stream->bandwidth; + fotg210_vdbg(fotg210, + "schedule devp %s ep%d%s-iso period %d start %d.%d\n", + urb->dev->devpath, stream->bEndpointAddress & 0x0f, + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", + urb->interval, + next_uframe >> 3, next_uframe & 0x7); + } + + /* fill iTDs uframe by uframe */ + for (packet = 0, itd = NULL; packet < urb->number_of_packets;) { + if (itd == NULL) { + /* ASSERT: we have all necessary itds */ + + /* ASSERT: no itds for this endpoint in this uframe */ + + itd = list_entry(iso_sched->td_list.next, + struct fotg210_itd, itd_list); + list_move_tail(&itd->itd_list, &stream->td_list); + itd->stream = stream; + itd->urb = urb; + itd_init(fotg210, stream, itd); + } + + uframe = next_uframe & 0x07; + frame = next_uframe >> 3; + + itd_patch(fotg210, itd, iso_sched, packet, uframe); + + next_uframe += stream->interval; + next_uframe &= mod - 1; + packet++; + + /* link completed itds into the schedule */ + if (((next_uframe >> 3) != frame) + || packet == urb->number_of_packets) { + itd_link(fotg210, frame & (fotg210->periodic_size - 1), + itd); + itd = NULL; + } + } + stream->next_uframe = next_uframe; + + /* don't need that schedule data any more */ + iso_sched_free(stream, iso_sched); + urb->hcpriv = NULL; + + ++fotg210->isoc_count; + enable_periodic(fotg210); +} + +#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ + FOTG210_ISOC_XACTERR) + +/* Process and recycle a completed ITD. Return true iff its urb completed, + * and hence its completion callback probably added things to the hardware + * schedule. + * + * Note that we carefully avoid recycling this descriptor until after any + * completion callback runs, so that it won't be reused quickly. That is, + * assuming (a) no more than two urbs per frame on this endpoint, and also + * (b) only this endpoint's completions submit URBs. It seems some silicon + * corrupts things if you reuse completed descriptors very quickly... + */ +static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) +{ + struct urb *urb = itd->urb; + struct usb_iso_packet_descriptor *desc; + u32 t; + unsigned uframe; + int urb_index = -1; + struct fotg210_iso_stream *stream = itd->stream; + struct usb_device *dev; + bool retval = false; + + /* for each uframe with a packet */ + for (uframe = 0; uframe < 8; uframe++) { + if (likely(itd->index[uframe] == -1)) + continue; + urb_index = itd->index[uframe]; + desc = &urb->iso_frame_desc[urb_index]; + + t = hc32_to_cpup(fotg210, &itd->hw_transaction[uframe]); + itd->hw_transaction[uframe] = 0; + + /* report transfer status */ + if (unlikely(t & ISO_ERRS)) { + urb->error_count++; + if (t & FOTG210_ISOC_BUF_ERR) + desc->status = usb_pipein(urb->pipe) + ? -ENOSR /* hc couldn't read */ + : -ECOMM; /* hc couldn't write */ + else if (t & FOTG210_ISOC_BABBLE) + desc->status = -EOVERFLOW; + else /* (t & FOTG210_ISOC_XACTERR) */ + desc->status = -EPROTO; + + /* HC need not update length with this error */ + if (!(t & FOTG210_ISOC_BABBLE)) { + desc->actual_length = + fotg210_itdlen(urb, desc, t); + urb->actual_length += desc->actual_length; + } + } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { + desc->status = 0; + desc->actual_length = fotg210_itdlen(urb, desc, t); + urb->actual_length += desc->actual_length; + } else { + /* URB was too late */ + desc->status = -EXDEV; + } + } + + /* handle completion now? */ + if (likely((urb_index + 1) != urb->number_of_packets)) + goto done; + + /* ASSERT: it's really the last itd for this urb + list_for_each_entry (itd, &stream->td_list, itd_list) + BUG_ON (itd->urb == urb); + */ + + /* give urb back to the driver; completion often (re)submits */ + dev = urb->dev; + fotg210_urb_done(fotg210, urb, 0); + retval = true; + urb = NULL; + + --fotg210->isoc_count; + disable_periodic(fotg210); + + if (unlikely(list_is_singular(&stream->td_list))) { + fotg210_to_hcd(fotg210)->self.bandwidth_allocated + -= stream->bandwidth; + fotg210_vdbg(fotg210, + "deschedule devp %s ep%d%s-iso\n", + dev->devpath, stream->bEndpointAddress & 0x0f, + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); + } + +done: + itd->urb = NULL; + + /* Add to the end of the free list for later reuse */ + list_move_tail(&itd->itd_list, &stream->free_list); + + /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ + if (list_empty(&stream->td_list)) { + list_splice_tail_init(&stream->free_list, + &fotg210->cached_itd_list); + start_free_itds(fotg210); + } + + return retval; +} + +/*-------------------------------------------------------------------------*/ + +static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, + gfp_t mem_flags) +{ + int status = -EINVAL; + unsigned long flags; + struct fotg210_iso_stream *stream; + + /* Get iso_stream head */ + stream = iso_stream_find(fotg210, urb); + if (unlikely(stream == NULL)) { + fotg210_dbg(fotg210, "can't get iso stream\n"); + return -ENOMEM; + } + if (unlikely(urb->interval != stream->interval && + fotg210_port_speed(fotg210, 0) == + USB_PORT_STAT_HIGH_SPEED)) { + fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", + stream->interval, urb->interval); + goto done; + } + +#ifdef FOTG210_URB_TRACE + fotg210_dbg(fotg210, + "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", + __func__, urb->dev->devpath, urb, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->transfer_buffer_length, + urb->number_of_packets, urb->interval, + stream); +#endif + + /* allocate ITDs w/o locking anything */ + status = itd_urb_transaction(stream, fotg210, urb, mem_flags); + if (unlikely(status < 0)) { + fotg210_dbg(fotg210, "can't init itds\n"); + goto done; + } + + /* schedule ... need to lock */ + spin_lock_irqsave(&fotg210->lock, flags); + if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { + status = -ESHUTDOWN; + goto done_not_linked; + } + status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); + if (unlikely(status)) + goto done_not_linked; + status = iso_stream_schedule(fotg210, urb, stream); + if (likely(status == 0)) + itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream); + else + usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); + done_not_linked: + spin_unlock_irqrestore(&fotg210->lock, flags); + done: + return status; +} + +/*-------------------------------------------------------------------------*/ + +static void scan_isoc(struct fotg210_hcd *fotg210) +{ + unsigned uf, now_frame, frame; + unsigned fmask = fotg210->periodic_size - 1; + bool modified, live; + + /* + * When running, scan from last scan point up to "now" + * else clean up by scanning everything that's left. + * Touches as few pages as possible: cache-friendly. + */ + if (fotg210->rh_state >= FOTG210_RH_RUNNING) { + uf = fotg210_read_frame_index(fotg210); + now_frame = (uf >> 3) & fmask; + live = true; + } else { + now_frame = (fotg210->next_frame - 1) & fmask; + live = false; + } + fotg210->now_frame = now_frame; + + frame = fotg210->next_frame; + for (;;) { + union fotg210_shadow q, *q_p; + __hc32 type, *hw_p; + +restart: + /* scan each element in frame's queue for completions */ + q_p = &fotg210->pshadow[frame]; + hw_p = &fotg210->periodic[frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE(fotg210, *hw_p); + modified = false; + + while (q.ptr != NULL) { + switch (hc32_to_cpu(fotg210, type)) { + case Q_TYPE_ITD: + /* If this ITD is still active, leave it for + * later processing ... check the next entry. + * No need to check for activity unless the + * frame is current. + */ + if (frame == now_frame && live) { + rmb(); + for (uf = 0; uf < 8; uf++) { + if (q.itd->hw_transaction[uf] & + ITD_ACTIVE(fotg210)) + break; + } + if (uf < 8) { + q_p = &q.itd->itd_next; + hw_p = &q.itd->hw_next; + type = Q_NEXT_TYPE(fotg210, + q.itd->hw_next); + q = *q_p; + break; + } + } + + /* Take finished ITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. HC won't cache the + * pointer for much longer, if at all. + */ + *q_p = q.itd->itd_next; + *hw_p = q.itd->hw_next; + type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); + wmb(); + modified = itd_complete(fotg210, q.itd); + q = *q_p; + break; + default: + fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", + type, frame, q.ptr); + /* FALL THROUGH */ + case Q_TYPE_QH: + case Q_TYPE_FSTN: + /* End of the iTDs and siTDs */ + q.ptr = NULL; + break; + } + + /* assume completion callbacks modify the queue */ + if (unlikely(modified && fotg210->isoc_count > 0)) + goto restart; + } + + /* Stop when we have reached the current frame */ + if (frame == now_frame) + break; + frame = (frame + 1) & fmask; + } + fotg210->next_frame = now_frame; +} +/*-------------------------------------------------------------------------*/ +/* + * Display / Set uframe_periodic_max + */ +static ssize_t show_uframe_periodic_max(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fotg210_hcd *fotg210; + int n; + + fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); + n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); + return n; +} + + +static ssize_t store_uframe_periodic_max(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fotg210_hcd *fotg210; + unsigned uframe_periodic_max; + unsigned frame, uframe; + unsigned short allocated_max; + unsigned long flags; + ssize_t ret; + + fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); + if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) + return -EINVAL; + + if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { + fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n", + uframe_periodic_max); + return -EINVAL; + } + + ret = -EINVAL; + + /* + * lock, so that our checking does not race with possible periodic + * bandwidth allocation through submitting new urbs. + */ + spin_lock_irqsave(&fotg210->lock, flags); + + /* + * for request to decrease max periodic bandwidth, we have to check + * every microframe in the schedule to see whether the decrease is + * possible. + */ + if (uframe_periodic_max < fotg210->uframe_periodic_max) { + allocated_max = 0; + + for (frame = 0; frame < fotg210->periodic_size; ++frame) + for (uframe = 0; uframe < 7; ++uframe) + allocated_max = max(allocated_max, + periodic_usecs(fotg210, frame, uframe)); + + if (allocated_max > uframe_periodic_max) { + fotg210_info(fotg210, + "cannot decrease uframe_periodic_max becase " + "periodic bandwidth is already allocated " + "(%u > %u)\n", + allocated_max, uframe_periodic_max); + goto out_unlock; + } + } + + /* increasing is always ok */ + + fotg210_info(fotg210, "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", + 100 * uframe_periodic_max/125, uframe_periodic_max); + + if (uframe_periodic_max != 100) + fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n"); + + fotg210->uframe_periodic_max = uframe_periodic_max; + ret = count; + +out_unlock: + spin_unlock_irqrestore(&fotg210->lock, flags); + return ret; +} + +static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, + store_uframe_periodic_max); + +static inline int create_sysfs_files(struct fotg210_hcd *fotg210) +{ + struct device *controller = fotg210_to_hcd(fotg210)->self.controller; + int i = 0; + + if (i) + goto out; + + i = device_create_file(controller, &dev_attr_uframe_periodic_max); +out: + return i; +} + +static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) +{ + struct device *controller = fotg210_to_hcd(fotg210)->self.controller; + + device_remove_file(controller, &dev_attr_uframe_periodic_max); +} +/*-------------------------------------------------------------------------*/ + +/* On some systems, leaving remote wakeup enabled prevents system shutdown. + * The firmware seems to think that powering off is a wakeup event! + * This routine turns off remote wakeup and everything else, on all ports. + */ +static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210) +{ + u32 __iomem *status_reg = &fotg210->regs->port_status; + + fotg210_writel(fotg210, PORT_RWC_BITS, status_reg); +} + +/* + * Halt HC, turn off all ports, and let the BIOS use the companion controllers. + * Must be called with interrupts enabled and the lock not held. + */ +static void fotg210_silence_controller(struct fotg210_hcd *fotg210) +{ + fotg210_halt(fotg210); + + spin_lock_irq(&fotg210->lock); + fotg210->rh_state = FOTG210_RH_HALTED; + fotg210_turn_off_all_ports(fotg210); + spin_unlock_irq(&fotg210->lock); +} + +/* fotg210_shutdown kick in for silicon on any bus (not just pci, etc). + * This forcibly disables dma and IRQs, helping kexec and other cases + * where the next system software may expect clean state. + */ +static void fotg210_shutdown(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + + spin_lock_irq(&fotg210->lock); + fotg210->shutdown = true; + fotg210->rh_state = FOTG210_RH_STOPPING; + fotg210->enabled_hrtimer_events = 0; + spin_unlock_irq(&fotg210->lock); + + fotg210_silence_controller(fotg210); + + hrtimer_cancel(&fotg210->hrtimer); +} + +/*-------------------------------------------------------------------------*/ + +/* + * fotg210_work is called from some interrupts, timers, and so on. + * it calls driver completion functions, after dropping fotg210->lock. + */ +static void fotg210_work(struct fotg210_hcd *fotg210) +{ + /* another CPU may drop fotg210->lock during a schedule scan while + * it reports urb completions. this flag guards against bogus + * attempts at re-entrant schedule scanning. + */ + if (fotg210->scanning) { + fotg210->need_rescan = true; + return; + } + fotg210->scanning = true; + + rescan: + fotg210->need_rescan = false; + if (fotg210->async_count) + scan_async(fotg210); + if (fotg210->intr_count > 0) + scan_intr(fotg210); + if (fotg210->isoc_count > 0) + scan_isoc(fotg210); + if (fotg210->need_rescan) + goto rescan; + fotg210->scanning = false; + + /* the IO watchdog guards against hardware or driver bugs that + * misplace IRQs, and should let us run completely without IRQs. + * such lossage has been observed on both VT6202 and VT8235. + */ + turn_on_io_watchdog(fotg210); +} + +/* + * Called when the fotg210_hcd module is removed. + */ +static void fotg210_stop(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + + fotg210_dbg(fotg210, "stop\n"); + + /* no more interrupts ... */ + + spin_lock_irq(&fotg210->lock); + fotg210->enabled_hrtimer_events = 0; + spin_unlock_irq(&fotg210->lock); + + fotg210_quiesce(fotg210); + fotg210_silence_controller(fotg210); + fotg210_reset(fotg210); + + hrtimer_cancel(&fotg210->hrtimer); + remove_sysfs_files(fotg210); + remove_debug_files(fotg210); + + /* root hub is shut down separately (first, when possible) */ + spin_lock_irq(&fotg210->lock); + end_free_itds(fotg210); + spin_unlock_irq(&fotg210->lock); + fotg210_mem_cleanup(fotg210); + +#ifdef FOTG210_STATS + fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n", + fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa, + fotg210->stats.lost_iaa); + fotg210_dbg(fotg210, "complete %ld unlink %ld\n", + fotg210->stats.complete, fotg210->stats.unlink); +#endif + + dbg_status(fotg210, "fotg210_stop completed", + fotg210_readl(fotg210, &fotg210->regs->status)); +} + +/* one-time init, only for memory state */ +static int hcd_fotg210_init(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp; + int retval; + u32 hcc_params; + struct fotg210_qh_hw *hw; + + spin_lock_init(&fotg210->lock); + + /* + * keep io watchdog by default, those good HCDs could turn off it later + */ + fotg210->need_io_watchdog = 1; + + hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + fotg210->hrtimer.function = fotg210_hrtimer_func; + fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; + + hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); + + /* + * by default set standard 80% (== 100 usec/uframe) max periodic + * bandwidth as required by USB 2.0 + */ + fotg210->uframe_periodic_max = 100; + + /* + * hw default: 1K periodic list heads, one per frame. + * periodic_size can shrink by USBCMD update if hcc_params allows. + */ + fotg210->periodic_size = DEFAULT_I_TDPS; + INIT_LIST_HEAD(&fotg210->intr_qh_list); + INIT_LIST_HEAD(&fotg210->cached_itd_list); + + if (HCC_PGM_FRAMELISTLEN(hcc_params)) { + /* periodic schedule size can be smaller than default */ + switch (FOTG210_TUNE_FLS) { + case 0: + fotg210->periodic_size = 1024; + break; + case 1: + fotg210->periodic_size = 512; + break; + case 2: + fotg210->periodic_size = 256; + break; + default: + BUG(); + } + } + retval = fotg210_mem_init(fotg210, GFP_KERNEL); + if (retval < 0) + return retval; + + /* controllers may cache some of the periodic schedule ... */ + fotg210->i_thresh = 2; + + /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * its dummy is used in hw_alt_next of many tds, to prevent the qh + * from automatically advancing to the next td after short reads. + */ + fotg210->async->qh_next.qh = NULL; + hw = fotg210->async->hw; + hw->hw_next = QH_NEXT(fotg210, fotg210->async->qh_dma); + hw->hw_info1 = cpu_to_hc32(fotg210, QH_HEAD); + hw->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); + hw->hw_qtd_next = FOTG210_LIST_END(fotg210); + fotg210->async->qh_state = QH_STATE_LINKED; + hw->hw_alt_next = QTD_NEXT(fotg210, fotg210->async->dummy->qtd_dma); + + /* clear interrupt enables, set irq latency */ + if (log2_irq_thresh < 0 || log2_irq_thresh > 6) + log2_irq_thresh = 0; + temp = 1 << (16 + log2_irq_thresh); + if (HCC_CANPARK(hcc_params)) { + /* HW default park == 3, on hardware that supports it (like + * NVidia and ALI silicon), maximizes throughput on the async + * schedule by avoiding QH fetches between transfers. + * + * With fast usb storage devices and NForce2, "park" seems to + * make problems: throughput reduction (!), data errors... + */ + if (park) { + park = min_t(unsigned, park, 3); + temp |= CMD_PARK; + temp |= park << 8; + } + fotg210_dbg(fotg210, "park %d\n", park); + } + if (HCC_PGM_FRAMELISTLEN(hcc_params)) { + /* periodic schedule size can be smaller than default */ + temp &= ~(3 << 2); + temp |= (FOTG210_TUNE_FLS << 2); + } + fotg210->command = temp; + + /* Accept arbitrarily long scatter-gather lists */ + if (!(hcd->driver->flags & HCD_LOCAL_MEM)) + hcd->self.sg_tablesize = ~0; + return 0; +} + +/* start HC running; it's halted, hcd_fotg210_init() has been run (once) */ +static int fotg210_run(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp; + u32 hcc_params; + + hcd->uses_new_polling = 1; + + /* EHCI spec section 4.1 */ + + fotg210_writel(fotg210, fotg210->periodic_dma, + &fotg210->regs->frame_list); + fotg210_writel(fotg210, (u32)fotg210->async->qh_dma, + &fotg210->regs->async_next); + + /* + * hcc_params controls whether fotg210->regs->segment must (!!!) + * be used; it constrains QH/ITD/SITD and QTD locations. + * pci_pool consistent memory always uses segment zero. + * streaming mappings for I/O buffers, like pci_map_single(), + * can return segments above 4GB, if the device allows. + * + * NOTE: the dma mask is visible through dma_supported(), so + * drivers can pass this info along ... like NETIF_F_HIGHDMA, + * Scsi_Host.highmem_io, and so forth. It's readonly to all + * host side drivers though. + */ + hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); + + /* + * Philips, Intel, and maybe others need CMD_RUN before the + * root hub will detect new devices (why?); NEC doesn't + */ + fotg210->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + fotg210->command |= CMD_RUN; + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + dbg_cmd(fotg210, "init", fotg210->command); + + /* + * Start, enabling full USB 2.0 functionality ... usb 1.1 devices + * are explicitly handed to companion controller(s), so no TT is + * involved with the root hub. (Except where one is integrated, + * and there's no companion controller unless maybe for USB OTG.) + * + * Turning on the CF flag will transfer ownership of all ports + * from the companions to the EHCI controller. If any of the + * companions are in the middle of a port reset at the time, it + * could cause trouble. Write-locking ehci_cf_port_reset_rwsem + * guarantees that no resets are in progress. After we set CF, + * a short delay lets the hardware catch up; new resets shouldn't + * be started before the port switching actions could complete. + */ + down_write(&ehci_cf_port_reset_rwsem); + fotg210->rh_state = FOTG210_RH_RUNNING; + /* unblock posted writes */ + fotg210_readl(fotg210, &fotg210->regs->command); + msleep(5); + up_write(&ehci_cf_port_reset_rwsem); + fotg210->last_periodic_enable = ktime_get_real(); + + temp = HC_VERSION(fotg210, + fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); + fotg210_info(fotg210, + "USB %x.%x started, EHCI %x.%02x\n", + ((fotg210->sbrn & 0xf0)>>4), (fotg210->sbrn & 0x0f), + temp >> 8, temp & 0xff); + + fotg210_writel(fotg210, INTR_MASK, + &fotg210->regs->intr_enable); /* Turn On Interrupts */ + + /* GRR this is run-once init(), being done every time the HC starts. + * So long as they're part of class devices, we can't do it init() + * since the class device isn't created that early. + */ + create_debug_files(fotg210); + create_sysfs_files(fotg210); + + return 0; +} + +static int fotg210_setup(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + int retval; + + fotg210->regs = (void __iomem *)fotg210->caps + + HC_LENGTH(fotg210, + fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); + dbg_hcs_params(fotg210, "reset"); + dbg_hcc_params(fotg210, "reset"); + + /* cache this readonly data; minimize chip reads */ + fotg210->hcs_params = fotg210_readl(fotg210, + &fotg210->caps->hcs_params); + + fotg210->sbrn = HCD_USB2; + + /* data structure init */ + retval = hcd_fotg210_init(hcd); + if (retval) + return retval; + + retval = fotg210_halt(fotg210); + if (retval) + return retval; + + fotg210_reset(fotg210); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static irqreturn_t fotg210_irq(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 status, masked_status, pcd_status = 0, cmd; + int bh; + + spin_lock(&fotg210->lock); + + status = fotg210_readl(fotg210, &fotg210->regs->status); + + /* e.g. cardbus physical eject */ + if (status == ~(u32) 0) { + fotg210_dbg(fotg210, "device removed\n"); + goto dead; + } + + /* + * We don't use STS_FLR, but some controllers don't like it to + * remain on, so mask it out along with the other status bits. + */ + masked_status = status & (INTR_MASK | STS_FLR); + + /* Shared IRQ? */ + if (!masked_status || + unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { + spin_unlock(&fotg210->lock); + return IRQ_NONE; + } + + /* clear (just) interrupts */ + fotg210_writel(fotg210, masked_status, &fotg210->regs->status); + cmd = fotg210_readl(fotg210, &fotg210->regs->command); + bh = 0; + +#ifdef VERBOSE_DEBUG + /* unrequested/ignored: Frame List Rollover */ + dbg_status(fotg210, "irq", status); +#endif + + /* INT, ERR, and IAA interrupt rates can be throttled */ + + /* normal [4.15.1.2] or error [4.15.1.1] completion */ + if (likely((status & (STS_INT|STS_ERR)) != 0)) { + if (likely((status & STS_ERR) == 0)) + COUNT(fotg210->stats.normal); + else + COUNT(fotg210->stats.error); + bh = 1; + } + + /* complete the unlinking of some qh [4.15.2.3] */ + if (status & STS_IAA) { + + /* Turn off the IAA watchdog */ + fotg210->enabled_hrtimer_events &= + ~BIT(FOTG210_HRTIMER_IAA_WATCHDOG); + + /* + * Mild optimization: Allow another IAAD to reset the + * hrtimer, if one occurs before the next expiration. + * In theory we could always cancel the hrtimer, but + * tests show that about half the time it will be reset + * for some other event anyway. + */ + if (fotg210->next_hrtimer_event == FOTG210_HRTIMER_IAA_WATCHDOG) + ++fotg210->next_hrtimer_event; + + /* guard against (alleged) silicon errata */ + if (cmd & CMD_IAAD) + fotg210_dbg(fotg210, "IAA with IAAD still set?\n"); + if (fotg210->async_iaa) { + COUNT(fotg210->stats.iaa); + end_unlink_async(fotg210); + } else + fotg210_dbg(fotg210, "IAA with nothing unlinked?\n"); + } + + /* remote wakeup [4.3.1] */ + if (status & STS_PCD) { + int pstatus; + u32 __iomem *status_reg = &fotg210->regs->port_status; + + /* kick root hub later */ + pcd_status = status; + + /* resume root hub? */ + if (fotg210->rh_state == FOTG210_RH_SUSPENDED) + usb_hcd_resume_root_hub(hcd); + + pstatus = fotg210_readl(fotg210, status_reg); + + if (test_bit(0, &fotg210->suspended_ports) && + ((pstatus & PORT_RESUME) || + !(pstatus & PORT_SUSPEND)) && + (pstatus & PORT_PE) && + fotg210->reset_done[0] == 0) { + + /* start 20 msec resume signaling from this port, + * and make khubd collect PORT_STAT_C_SUSPEND to + * stop that signaling. Use 5 ms extra for safety, + * like usb_port_resume() does. + */ + fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25); + set_bit(0, &fotg210->resuming_ports); + fotg210_dbg(fotg210, "port 1 remote wakeup\n"); + mod_timer(&hcd->rh_timer, fotg210->reset_done[0]); + } + } + + /* PCI errors [4.15.2.4] */ + if (unlikely((status & STS_FATAL) != 0)) { + fotg210_err(fotg210, "fatal error\n"); + dbg_cmd(fotg210, "fatal", cmd); + dbg_status(fotg210, "fatal", status); +dead: + usb_hc_died(hcd); + + /* Don't let the controller do anything more */ + fotg210->shutdown = true; + fotg210->rh_state = FOTG210_RH_STOPPING; + fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); + fotg210_writel(fotg210, fotg210->command, + &fotg210->regs->command); + fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); + fotg210_handle_controller_death(fotg210); + + /* Handle completions when the controller stops */ + bh = 0; + } + + if (bh) + fotg210_work(fotg210); + spin_unlock(&fotg210->lock); + if (pcd_status) + usb_hcd_poll_rh_status(hcd); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +/* + * non-error returns are a promise to giveback() the urb later + * we drop ownership so next owner (or urb unlink) can get it + * + * urb + dev is in hcd.self.controller.urb_list + * we're queueing TDs onto software and hardware lists + * + * hcd-specific init for hcpriv hasn't been done yet + * + * NOTE: control, bulk, and interrupt share the same code to append TDs + * to a (possibly active) QH, and the same QH scanning code. + */ +static int fotg210_urb_enqueue( + struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags +) { + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct list_head qtd_list; + + INIT_LIST_HEAD(&qtd_list); + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + /* qh_completions() code doesn't handle all the fault cases + * in multi-TD control transfers. Even 1KB is rare anyway. + */ + if (urb->transfer_buffer_length > (16 * 1024)) + return -EMSGSIZE; + /* FALLTHROUGH */ + /* case PIPE_BULK: */ + default: + if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return submit_async(fotg210, urb, &qtd_list, mem_flags); + + case PIPE_INTERRUPT: + if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return intr_submit(fotg210, urb, &qtd_list, mem_flags); + + case PIPE_ISOCHRONOUS: + return itd_submit(fotg210, urb, mem_flags); + } +} + +/* remove from hardware lists + * completions normally happen asynchronously + */ + +static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_qh *qh; + unsigned long flags; + int rc; + + spin_lock_irqsave(&fotg210->lock, flags); + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) + goto done; + + switch (usb_pipetype(urb->pipe)) { + /* case PIPE_CONTROL: */ + /* case PIPE_BULK:*/ + default: + qh = (struct fotg210_qh *) urb->hcpriv; + if (!qh) + break; + switch (qh->qh_state) { + case QH_STATE_LINKED: + case QH_STATE_COMPLETING: + start_unlink_async(fotg210, qh); + break; + case QH_STATE_UNLINK: + case QH_STATE_UNLINK_WAIT: + /* already started */ + break; + case QH_STATE_IDLE: + /* QH might be waiting for a Clear-TT-Buffer */ + qh_completions(fotg210, qh); + break; + } + break; + + case PIPE_INTERRUPT: + qh = (struct fotg210_qh *) urb->hcpriv; + if (!qh) + break; + switch (qh->qh_state) { + case QH_STATE_LINKED: + case QH_STATE_COMPLETING: + start_unlink_intr(fotg210, qh); + break; + case QH_STATE_IDLE: + qh_completions(fotg210, qh); + break; + default: + fotg210_dbg(fotg210, "bogus qh %p state %d\n", + qh, qh->qh_state); + goto done; + } + break; + + case PIPE_ISOCHRONOUS: + /* itd... */ + + /* wait till next completion, do it then. */ + /* completion irqs can wait up to 1024 msec, */ + break; + } +done: + spin_unlock_irqrestore(&fotg210->lock, flags); + return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* bulk qh holds the data toggle */ + +static void +fotg210_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + unsigned long flags; + struct fotg210_qh *qh, *tmp; + + /* ASSERT: any requests/urbs are being unlinked */ + /* ASSERT: nobody can be submitting urbs for this any more */ + +rescan: + spin_lock_irqsave(&fotg210->lock, flags); + qh = ep->hcpriv; + if (!qh) + goto done; + + /* endpoints can be iso streams. for now, we don't + * accelerate iso completions ... so spin a while. + */ + if (qh->hw == NULL) { + struct fotg210_iso_stream *stream = ep->hcpriv; + + if (!list_empty(&stream->td_list)) + goto idle_timeout; + + /* BUG_ON(!list_empty(&stream->free_list)); */ + kfree(stream); + goto done; + } + + if (fotg210->rh_state < FOTG210_RH_RUNNING) + qh->qh_state = QH_STATE_IDLE; + switch (qh->qh_state) { + case QH_STATE_LINKED: + case QH_STATE_COMPLETING: + for (tmp = fotg210->async->qh_next.qh; + tmp && tmp != qh; + tmp = tmp->qh_next.qh) + continue; + /* periodic qh self-unlinks on empty, and a COMPLETING qh + * may already be unlinked. + */ + if (tmp) + start_unlink_async(fotg210, qh); + /* FALL THROUGH */ + case QH_STATE_UNLINK: /* wait for hw to finish? */ + case QH_STATE_UNLINK_WAIT: +idle_timeout: + spin_unlock_irqrestore(&fotg210->lock, flags); + schedule_timeout_uninterruptible(1); + goto rescan; + case QH_STATE_IDLE: /* fully unlinked */ + if (qh->clearing_tt) + goto idle_timeout; + if (list_empty(&qh->qtd_list)) { + qh_destroy(fotg210, qh); + break; + } + /* else FALL THROUGH */ + default: + /* caller was supposed to have unlinked any requests; + * that's not our job. just leak this memory. + */ + fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n", + qh, ep->desc.bEndpointAddress, qh->qh_state, + list_empty(&qh->qtd_list) ? "" : "(has tds)"); + break; + } + done: + ep->hcpriv = NULL; + spin_unlock_irqrestore(&fotg210->lock, flags); +} + +static void +fotg210_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_qh *qh; + int eptype = usb_endpoint_type(&ep->desc); + int epnum = usb_endpoint_num(&ep->desc); + int is_out = usb_endpoint_dir_out(&ep->desc); + unsigned long flags; + + if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) + return; + + spin_lock_irqsave(&fotg210->lock, flags); + qh = ep->hcpriv; + + /* For Bulk and Interrupt endpoints we maintain the toggle state + * in the hardware; the toggle bits in udev aren't used at all. + * When an endpoint is reset by usb_clear_halt() we must reset + * the toggle bit in the QH. + */ + if (qh) { + usb_settoggle(qh->dev, epnum, is_out, 0); + if (!list_empty(&qh->qtd_list)) { + WARN_ONCE(1, "clear_halt for a busy endpoint\n"); + } else if (qh->qh_state == QH_STATE_LINKED || + qh->qh_state == QH_STATE_COMPLETING) { + + /* The toggle value in the QH can't be updated + * while the QH is active. Unlink it now; + * re-linking will call qh_refresh(). + */ + if (eptype == USB_ENDPOINT_XFER_BULK) + start_unlink_async(fotg210, qh); + else + start_unlink_intr(fotg210, qh); + } + } + spin_unlock_irqrestore(&fotg210->lock, flags); +} + +static int fotg210_get_frame(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + return (fotg210_read_frame_index(fotg210) >> 3) % + fotg210->periodic_size; +} + +/*-------------------------------------------------------------------------*/ + +/* + * The EHCI in ChipIdea HDRC cannot be a separate module or device, + * because its registers (and irq) are shared between host/gadget/otg + * functions and in order to facilitate role switching we cannot + * give the fotg210 driver exclusive access to those. + */ +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); + +static const struct hc_driver fotg210_fotg210_hc_driver = { + .description = hcd_name, + .product_desc = "Faraday USB2.0 Host Controller", + .hcd_priv_size = sizeof(struct fotg210_hcd), + + /* + * generic hardware linkage + */ + .irq = fotg210_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = hcd_fotg210_init, + .start = fotg210_run, + .stop = fotg210_stop, + .shutdown = fotg210_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = fotg210_urb_enqueue, + .urb_dequeue = fotg210_urb_dequeue, + .endpoint_disable = fotg210_endpoint_disable, + .endpoint_reset = fotg210_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = fotg210_get_frame, + + /* + * root hub support + */ + .hub_status_data = fotg210_hub_status_data, + .hub_control = fotg210_hub_control, + .bus_suspend = fotg210_bus_suspend, + .bus_resume = fotg210_bus_resume, + + .relinquish_port = fotg210_relinquish_port, + .port_handed_over = fotg210_port_handed_over, + + .clear_tt_buffer_complete = fotg210_clear_tt_buffer_complete, +}; + +static void fotg210_init(struct fotg210_hcd *fotg210) +{ + u32 value; + + iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, + &fotg210->regs->gmir); + + value = ioread32(&fotg210->regs->otgcsr); + value &= ~OTGCSR_A_BUS_DROP; + value |= OTGCSR_A_BUS_REQ; + iowrite32(value, &fotg210->regs->otgcsr); +} + +/** + * fotg210_hcd_probe - initialize faraday FOTG210 HCDs + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +static int fotg210_hcd_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; + struct resource *res; + int irq; + int retval = -ENODEV; + struct fotg210_hcd *fotg210; + + if (usb_disabled()) + return -ENODEV; + + pdev->dev.power.power_state = PMSG_ON; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, + "Found HC with no IRQ. Check %s setup!\n", + dev_name(dev)); + return -ENODEV; + } + + irq = res->start; + + hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, + dev_name(dev)); + if (!hcd) { + dev_err(dev, "failed to create hcd with err %d\n", retval); + retval = -ENOMEM; + goto fail_create_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(dev)); + retval = -ENODEV; + goto fail_request_resource; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->has_tt = 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + fotg210_fotg210_hc_driver.description)) { + dev_dbg(dev, "controller already in use\n"); + retval = -EBUSY; + goto fail_request_resource; + } + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(dev)); + retval = -ENODEV; + goto fail_request_resource; + } + + hcd->regs = ioremap_nocache(res->start, resource_size(res)); + if (hcd->regs == NULL) { + dev_dbg(dev, "error mapping memory\n"); + retval = -EFAULT; + goto fail_ioremap; + } + + fotg210 = hcd_to_fotg210(hcd); + + fotg210->caps = hcd->regs; + + retval = fotg210_setup(hcd); + if (retval) + goto fail_add_hcd; + + fotg210_init(fotg210); + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval) { + dev_err(dev, "failed to add hcd with err %d\n", retval); + goto fail_add_hcd; + } + + return retval; + +fail_add_hcd: + iounmap(hcd->regs); +fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +fail_request_resource: + usb_put_hcd(hcd); +fail_create_hcd: + dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); + return retval; +} + +/** + * fotg210_hcd_remove - shutdown processing for EHCI HCDs + * @dev: USB Host Controller being removed + * + */ +static int fotg210_hcd_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); + + if (!hcd) + return 0; + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + return 0; +} + +static struct platform_driver fotg210_hcd_driver = { + .driver = { + .name = "fotg210-hcd", + }, + .probe = fotg210_hcd_probe, + .remove = fotg210_hcd_remove, +}; + +static int __init fotg210_hcd_init(void) +{ + int retval = 0; + + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); + if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || + test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) + pr_warn(KERN_WARNING "Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); + + pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd\n", + hcd_name, + sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd), + sizeof(struct fotg210_itd)); + +#ifdef DEBUG + fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); + if (!fotg210_debug_root) { + retval = -ENOENT; + goto err_debug; + } +#endif + + retval = platform_driver_register(&fotg210_hcd_driver); + if (retval < 0) + goto clean; + return retval; + + platform_driver_unregister(&fotg210_hcd_driver); +clean: +#ifdef DEBUG + debugfs_remove(fotg210_debug_root); + fotg210_debug_root = NULL; +err_debug: +#endif + clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); + return retval; +} +module_init(fotg210_hcd_init); + +static void __exit fotg210_hcd_cleanup(void) +{ + platform_driver_unregister(&fotg210_hcd_driver); +#ifdef DEBUG + debugfs_remove(fotg210_debug_root); +#endif + clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); +} +module_exit(fotg210_hcd_cleanup); diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h new file mode 100644 index 000000000000..8920f9d32564 --- /dev/null +++ b/drivers/usb/host/fotg210.h @@ -0,0 +1,750 @@ +#ifndef __LINUX_FOTG210_H +#define __LINUX_FOTG210_H + +/* definitions used for the EHCI driver */ + +/* + * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to + * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on + * the host controller implementation. + * + * To facilitate the strongest possible byte-order checking from "sparse" + * and so on, we use __leXX unless that's not practical. + */ +#define __hc32 __le32 +#define __hc16 __le16 + +/* statistics can be kept for tuning/monitoring */ +struct fotg210_stats { + /* irq usage */ + unsigned long normal; + unsigned long error; + unsigned long iaa; + unsigned long lost_iaa; + + /* termination of urbs from core */ + unsigned long complete; + unsigned long unlink; +}; + +/* fotg210_hcd->lock guards shared data against other CPUs: + * fotg210_hcd: async, unlink, periodic (and shadow), ... + * usb_host_endpoint: hcpriv + * fotg210_qh: qh_next, qtd_list + * fotg210_qtd: qtd_list + * + * Also, hold this lock when talking to HC registers or + * when updating hw_* fields in shared qh/qtd/... structures. + */ + +#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ + +/* + * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the + * controller may be doing DMA. Lower values mean there's no DMA. + */ +enum fotg210_rh_state { + FOTG210_RH_HALTED, + FOTG210_RH_SUSPENDED, + FOTG210_RH_RUNNING, + FOTG210_RH_STOPPING +}; + +/* + * Timer events, ordered by increasing delay length. + * Always update event_delays_ns[] and event_handlers[] (defined in + * ehci-timer.c) in parallel with this list. + */ +enum fotg210_hrtimer_event { + FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */ + FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ + FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ + FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ + FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ + FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ + FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ + FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ + FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ + FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ + FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ +}; +#define FOTG210_HRTIMER_NO_EVENT 99 + +struct fotg210_hcd { /* one per controller */ + /* timing support */ + enum fotg210_hrtimer_event next_hrtimer_event; + unsigned enabled_hrtimer_events; + ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS]; + struct hrtimer hrtimer; + + int PSS_poll_count; + int ASS_poll_count; + int died_poll_count; + + /* glue to PCI and HCD framework */ + struct fotg210_caps __iomem *caps; + struct fotg210_regs __iomem *regs; + struct fotg210_dbg_port __iomem *debug; + + __u32 hcs_params; /* cached register copy */ + spinlock_t lock; + enum fotg210_rh_state rh_state; + + /* general schedule support */ + bool scanning:1; + bool need_rescan:1; + bool intr_unlinking:1; + bool async_unlinking:1; + bool shutdown:1; + struct fotg210_qh *qh_scan_next; + + /* async schedule support */ + struct fotg210_qh *async; + struct fotg210_qh *dummy; /* For AMD quirk use */ + struct fotg210_qh *async_unlink; + struct fotg210_qh *async_unlink_last; + struct fotg210_qh *async_iaa; + unsigned async_unlink_cycle; + unsigned async_count; /* async activity count */ + + /* periodic schedule support */ +#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ + unsigned periodic_size; + __hc32 *periodic; /* hw periodic table */ + dma_addr_t periodic_dma; + struct list_head intr_qh_list; + unsigned i_thresh; /* uframes HC might cache */ + + union fotg210_shadow *pshadow; /* mirror hw periodic table */ + struct fotg210_qh *intr_unlink; + struct fotg210_qh *intr_unlink_last; + unsigned intr_unlink_cycle; + unsigned now_frame; /* frame from HC hardware */ + unsigned next_frame; /* scan periodic, start here */ + unsigned intr_count; /* intr activity count */ + unsigned isoc_count; /* isoc activity count */ + unsigned periodic_count; /* periodic activity count */ + /* max periodic time per uframe */ + unsigned uframe_periodic_max; + + + /* list of itds completed while now_frame was still active */ + struct list_head cached_itd_list; + struct fotg210_itd *last_itd_to_free; + + /* per root hub port */ + unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; + + /* bit vectors (one bit per port) */ + unsigned long bus_suspended; /* which ports were + already suspended at the start of a bus suspend */ + unsigned long companion_ports; /* which ports are + dedicated to the companion controller */ + unsigned long owned_ports; /* which ports are + owned by the companion during a bus suspend */ + unsigned long port_c_suspend; /* which ports have + the change-suspend feature turned on */ + unsigned long suspended_ports; /* which ports are + suspended */ + unsigned long resuming_ports; /* which ports have + started to resume */ + + /* per-HC memory pools (could be per-bus, but ...) */ + struct dma_pool *qh_pool; /* qh per active urb */ + struct dma_pool *qtd_pool; /* one or more per qh */ + struct dma_pool *itd_pool; /* itd per iso urb */ + + unsigned random_frame; + unsigned long next_statechange; + ktime_t last_periodic_enable; + u32 command; + + /* SILICON QUIRKS */ + unsigned need_io_watchdog:1; + unsigned fs_i_thresh:1; /* Intel iso scheduling */ + + u8 sbrn; /* packed release number */ + + /* irq statistics */ +#ifdef FOTG210_STATS + struct fotg210_stats stats; +# define COUNT(x) ((x)++) +#else +# define COUNT(x) +#endif + + /* debug files */ +#ifdef DEBUG + struct dentry *debug_dir; +#endif +}; + +/* convert between an HCD pointer and the corresponding FOTG210_HCD */ +static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd) +{ + return (struct fotg210_hcd *)(hcd->hcd_priv); +} +static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210) +{ + return container_of((void *) fotg210, struct usb_hcd, hcd_priv); +} + +/*-------------------------------------------------------------------------*/ + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct fotg210_caps { + /* these fields are specified as 8 and 16 bit registers, + * but some hosts can't perform 8 or 16 bit PCI accesses. + * some hosts treat caplength and hciversion as parts of a 32-bit + * register, others treat them as two separate registers, this + * affects the memory map for big endian controllers. + */ + u32 hc_capbase; +#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ + (fotg210_big_endian_capbase(fotg210) ? 24 : 0))) +#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ + (fotg210_big_endian_capbase(fotg210) ? 0 : 16))) + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ + u8 portroute[8]; /* nibbles for routing - offset 0xC */ +}; + + +/* Section 2.3 Host Controller Operational Registers */ +struct fotg210_regs { + + /* USBCMD: offset 0x00 */ + u32 command; + +/* EHCI 1.1 addendum */ +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved1; + /* PORTSC: offset 0x20 */ + u32 port_status; +/* 31:23 reserved */ +#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) + u32 reserved2[19]; + + /* OTGCSR: offet 0x70 */ + u32 otgcsr; +#define OTGCSR_HOST_SPD_TYP (3 << 22) +#define OTGCSR_A_BUS_DROP (1 << 5) +#define OTGCSR_A_BUS_REQ (1 << 4) + + /* OTGISR: offset 0x74 */ + u32 otgisr; +#define OTGISR_OVC (1 << 10) + + u32 reserved3[15]; + + /* GMIR: offset 0xB4 */ + u32 gmir; +#define GMIR_INT_POLARITY (1 << 3) /*Active High*/ +#define GMIR_MHC_INT (1 << 2) +#define GMIR_MOTG_INT (1 << 1) +#define GMIR_MDEV_INT (1 << 0) +}; + +/* Appendix C, Debug port ... intended for use with special "debug devices" + * that can help if there's no serial console. (nonstandard enumeration.) + */ +struct fotg210_dbg_port { + u32 control; +#define DBGP_OWNER (1<<30) +#define DBGP_ENABLED (1<<28) +#define DBGP_DONE (1<<16) +#define DBGP_INUSE (1<<10) +#define DBGP_ERRCODE(x) (((x)>>7)&0x07) +# define DBGP_ERR_BAD 1 +# define DBGP_ERR_SIGNAL 2 +#define DBGP_ERROR (1<<6) +#define DBGP_GO (1<<5) +#define DBGP_OUT (1<<4) +#define DBGP_LEN(x) (((x)>>0)&0x0f) + u32 pids; +#define DBGP_PID_GET(x) (((x)>>16)&0xff) +#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) + u32 data03; + u32 data47; + u32 address; +#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) +}; + +#ifdef CONFIG_EARLY_PRINTK_DBGP +#include +extern int __init early_dbgp_init(char *s); +extern struct console early_dbgp_console; +#endif /* CONFIG_EARLY_PRINTK_DBGP */ + +struct usb_hcd; + +static inline int xen_dbgp_reset_prep(struct usb_hcd *hcd) +{ + return 1; /* Shouldn't this be 0? */ +} + +static inline int xen_dbgp_external_startup(struct usb_hcd *hcd) +{ + return -1; +} + +#ifdef CONFIG_EARLY_PRINTK_DBGP +/* Call backs from fotg210 host driver to fotg210 debug driver */ +extern int dbgp_external_startup(struct usb_hcd *); +extern int dbgp_reset_prep(struct usb_hcd *hcd); +#else +static inline int dbgp_reset_prep(struct usb_hcd *hcd) +{ + return xen_dbgp_reset_prep(hcd); +} +static inline int dbgp_external_startup(struct usb_hcd *hcd) +{ + return xen_dbgp_external_startup(hcd); +} +#endif + +/*-------------------------------------------------------------------------*/ + +#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma) + +/* + * EHCI Specification 0.95 Section 3.5 + * QTD: describe data transfer components (buffer, direction, ...) + * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". + * + * These are associated only with "QH" (Queue Head) structures, + * used with control, bulk, and interrupt transfers. + */ +struct fotg210_qtd { + /* first part defined by EHCI spec */ + __hc32 hw_next; /* see EHCI 3.5.1 */ + __hc32 hw_alt_next; /* see EHCI 3.5.2 */ + __hc32 hw_token; /* see EHCI 3.5.3 */ +#define QTD_TOGGLE (1 << 31) /* data toggle */ +#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define QTD_IOC (1 << 15) /* interrupt on complete */ +#define QTD_CERR(tok) (((tok)>>10) & 0x3) +#define QTD_PID(tok) (((tok)>>8) & 0x3) +#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define QTD_STS_HALT (1 << 6) /* halted on error */ +#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define QTD_STS_STS (1 << 1) /* split transaction state */ +#define QTD_STS_PING (1 << 0) /* issue PING? */ + +#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE) +#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT) +#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS) + + __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ + __hc32 hw_buf_hi[5]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t qtd_dma; /* qtd address */ + struct list_head qtd_list; /* sw qtd list */ + struct urb *urb; /* qtd's urb */ + size_t length; /* length of buffer */ +} __aligned(32); + +/* mask NakCnt+T in qh->hw_alt_next */ +#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f) + +#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) + +/*-------------------------------------------------------------------------*/ + +/* type tag from {qh,itd,fstn}->hw_next */ +#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1)) + +/* + * Now the following defines are not converted using the + * cpu_to_le32() macro anymore, since we have to support + * "dynamic" switching between be and le support, so that the driver + * can be used on one system with SoC EHCI controller using big-endian + * descriptors as well as a normal little-endian PCI EHCI controller. + */ +/* values for that type tag */ +#define Q_TYPE_ITD (0 << 1) +#define Q_TYPE_QH (1 << 1) +#define Q_TYPE_SITD (2 << 1) +#define Q_TYPE_FSTN (3 << 1) + +/* next async queue entry, or pointer to interrupt/periodic QH */ +#define QH_NEXT(fotg210, dma) \ + (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH)) + +/* for periodic/async schedules and qtd lists, mark end of list */ +#define FOTG210_LIST_END(fotg210) \ + cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */ + +/* + * Entries in periodic shadow table are pointers to one of four kinds + * of data structure. That's dictated by the hardware; a type tag is + * encoded in the low bits of the hardware's periodic schedule. Use + * Q_NEXT_TYPE to get the tag. + * + * For entries in the async schedule, the type tag always says "qh". + */ +union fotg210_shadow { + struct fotg210_qh *qh; /* Q_TYPE_QH */ + struct fotg210_itd *itd; /* Q_TYPE_ITD */ + struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ + __hc32 *hw_next; /* (all types) */ + void *ptr; +}; + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.6 + * QH: describes control/bulk/interrupt endpoints + * See Fig 3-7 "Queue Head Structure Layout". + * + * These appear in both the async and (for interrupt) periodic schedules. + */ + +/* first part defined by EHCI spec */ +struct fotg210_qh_hw { + __hc32 hw_next; /* see EHCI 3.6.1 */ + __hc32 hw_info1; /* see EHCI 3.6.2 */ +#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ +#define QH_HEAD (1 << 15) /* Head of async reclamation list */ +#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ +#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ +#define QH_LOW_SPEED (1 << 12) +#define QH_FULL_SPEED (0 << 12) +#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ + __hc32 hw_info2; /* see EHCI 3.6.2 */ +#define QH_SMASK 0x000000ff +#define QH_CMASK 0x0000ff00 +#define QH_HUBADDR 0x007f0000 +#define QH_HUBPORT 0x3f800000 +#define QH_MULT 0xc0000000 + __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ + + /* qtd overlay (hardware parts of a struct fotg210_qtd) */ + __hc32 hw_qtd_next; + __hc32 hw_alt_next; + __hc32 hw_token; + __hc32 hw_buf[5]; + __hc32 hw_buf_hi[5]; +} __aligned(32); + +struct fotg210_qh { + struct fotg210_qh_hw *hw; /* Must come first */ + /* the rest is HCD-private */ + dma_addr_t qh_dma; /* address of qh */ + union fotg210_shadow qh_next; /* ptr to qh; or periodic */ + struct list_head qtd_list; /* sw qtd list */ + struct list_head intr_node; /* list of intr QHs */ + struct fotg210_qtd *dummy; + struct fotg210_qh *unlink_next; /* next on unlink list */ + + unsigned unlink_cycle; + + u8 needs_rescan; /* Dequeue during giveback */ + u8 qh_state; +#define QH_STATE_LINKED 1 /* HC sees this */ +#define QH_STATE_UNLINK 2 /* HC may still see this */ +#define QH_STATE_IDLE 3 /* HC doesn't see this */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ +#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ + + u8 xacterrs; /* XactErr retry counter */ +#define QH_XACTERR_MAX 32 /* XactErr retry limit */ + + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + u16 tt_usecs; /* tt downstream bandwidth */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + + struct usb_device *dev; /* access to TT */ + unsigned is_out:1; /* bulk or intr OUT */ + unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ +}; + +/*-------------------------------------------------------------------------*/ + +/* description of one iso transaction (up to 3 KB data if highspeed) */ +struct fotg210_iso_packet { + /* These will be copied to iTD when scheduling */ + u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ + __hc32 transaction; /* itd->hw_transaction[i] |= */ + u8 cross; /* buf crosses pages */ + /* for full speed OUT splits */ + u32 buf1; +}; + +/* temporary schedule data for packets from iso urbs (both speeds) + * each packet is one logical usb transaction to the device (not TT), + * beginning at stream->next_uframe + */ +struct fotg210_iso_sched { + struct list_head td_list; + unsigned span; + struct fotg210_iso_packet packet[0]; +}; + +/* + * fotg210_iso_stream - groups all (s)itds for this endpoint. + * acts like a qh would, if EHCI had them for ISO. + */ +struct fotg210_iso_stream { + /* first field matches fotg210_hq, but is NULL */ + struct fotg210_qh_hw *hw; + + u8 bEndpointAddress; + u8 highspeed; + struct list_head td_list; /* queued itds */ + struct list_head free_list; /* list of unused itds */ + struct usb_device *udev; + struct usb_host_endpoint *ep; + + /* output of (re)scheduling */ + int next_uframe; + __hc32 splits; + + /* the rest is derived from the endpoint descriptor, + * trusting urb->interval == f(epdesc->bInterval) and + * including the extra info for hw_bufp[0..2] + */ + u8 usecs, c_usecs; + u16 interval; + u16 tt_usecs; + u16 maxp; + u16 raw_mask; + unsigned bandwidth; + + /* This is used to initialize iTD's hw_bufp fields */ + __hc32 buf0; + __hc32 buf1; + __hc32 buf2; + + /* this is used to initialize sITD's tt info */ + __hc32 address; +}; + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.3 + * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" + * + * Schedule records for high speed iso xfers + */ +struct fotg210_itd { + /* first part defined by EHCI spec */ + __hc32 hw_next; /* see EHCI 3.3.1 */ + __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ +#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ +#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */ +#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */ +#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ +#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) +#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */ + +#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE) + + __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ + __hc32 hw_bufp_hi[7]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t itd_dma; /* for this itd */ + union fotg210_shadow itd_next; /* ptr to periodic q entry */ + + struct urb *urb; + struct fotg210_iso_stream *stream; /* endpoint's queue */ + struct list_head itd_list; /* list of stream's itds */ + + /* any/all hw_transactions here may be used by that urb */ + unsigned frame; /* where scheduled */ + unsigned pg; + unsigned index[8]; /* in urb->iso_frame_desc */ +} __aligned(32); + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.96 Section 3.7 + * Periodic Frame Span Traversal Node (FSTN) + * + * Manages split interrupt transactions (using TT) that span frame boundaries + * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN + * makes the HC jump (back) to a QH to scan for fs/ls QH completions until + * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. + */ +struct fotg210_fstn { + __hc32 hw_next; /* any periodic q entry */ + __hc32 hw_prev; /* qh or FOTG210_LIST_END */ + + /* the rest is HCD-private */ + dma_addr_t fstn_dma; + union fotg210_shadow fstn_next; /* ptr to periodic q entry */ +} __aligned(32); + +/*-------------------------------------------------------------------------*/ + +/* Prepare the PORTSC wakeup flags during controller suspend/resume */ + +#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ + fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup); + +#define fotg210_prepare_ports_for_controller_resume(fotg210) \ + fotg210_adjust_port_wakeup_flags(fotg210, false, false); + +/*-------------------------------------------------------------------------*/ + +/* + * Some EHCI controllers have a Transaction Translator built into the + * root hub. This is a non-standard feature. Each controller will need + * to add code to the following inline functions, and call them as + * needed (mostly in root hub code). + */ + +static inline unsigned int +fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc) +{ + return (readl(&fotg210->regs->otgcsr) + & OTGCSR_HOST_SPD_TYP) >> 22; +} + +/* Returns the speed of a device attached to a port on the root hub. */ +static inline unsigned int +fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc) +{ + switch (fotg210_get_speed(fotg210, portsc)) { + case 0: + return 0; + case 1: + return USB_PORT_STAT_LOW_SPEED; + case 2: + default: + return USB_PORT_STAT_HIGH_SPEED; + } +} + +/*-------------------------------------------------------------------------*/ + +#define fotg210_has_fsl_portno_bug(e) (0) + +/* + * While most USB host controllers implement their registers in + * little-endian format, a minority (celleb companion chip) implement + * them in big endian format. + * + * This attempts to support either format at compile time without a + * runtime penalty, or both formats with the additional overhead + * of checking a flag bit. + * + */ + +#define fotg210_big_endian_mmio(e) 0 +#define fotg210_big_endian_capbase(e) 0 + +static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210, + __u32 __iomem *regs) +{ + return readl(regs); +} + +static inline void fotg210_writel(const struct fotg210_hcd *fotg210, + const unsigned int val, __u32 __iomem *regs) +{ + writel(val, regs); +} + +/* cpu to fotg210 */ +static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x) +{ + return cpu_to_le32(x); +} + +/* fotg210 to cpu */ +static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x) +{ + return le32_to_cpu(x); +} + +static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210, + const __hc32 *x) +{ + return le32_to_cpup(x); +} + +/*-------------------------------------------------------------------------*/ + +static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) +{ + return fotg210_readl(fotg210, &fotg210->regs->frame_index); +} + +#define fotg210_itdlen(urb, desc, t) ({ \ + usb_pipein((urb)->pipe) ? \ + (desc)->length - FOTG210_ITD_LENGTH(t) : \ + FOTG210_ITD_LENGTH(t); \ +}) +/*-------------------------------------------------------------------------*/ + +#ifndef DEBUG +#define STUB_DEBUG_FILES +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------*/ + +#endif /* __LINUX_FOTG210_H */ -- cgit v1.2.3-58-ga151 From 5447e0a652ba8907a0f4d88821acf7d37ef81af1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Jul 2013 16:10:58 -0700 Subject: Revert "usb: host: Faraday fotg210-hcd driver" This reverts commit 1dd3d123239179fad5de5dc00a6e0014a1918fde. The email address for the developer now bounces, which means they have moved on, so remove the driver until someone else from the company steps up to maintain it. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 - drivers/usb/host/Kconfig | 12 - drivers/usb/host/Makefile | 1 - drivers/usb/host/fotg210-hcd.c | 6049 ---------------------------------------- drivers/usb/host/fotg210.h | 750 ----- 5 files changed, 6813 deletions(-) delete mode 100644 drivers/usb/host/fotg210-hcd.c delete mode 100644 drivers/usb/host/fotg210.h (limited to 'drivers/usb') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 5460abf045de..238c5d47cadb 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ obj-$(CONFIG_USB_FUSBH200_HCD) += host/ -obj-$(CONFIG_USB_FOTG210_HCD) += host/ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index cf521d6551dd..4263d011392c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -354,18 +354,6 @@ config USB_FUSBH200_HCD To compile this driver as a module, choose M here: the module will be called fusbh200-hcd. -config USB_FOTG210_HCD - tristate "FOTG210 HCD support" - depends on USB - default N - ---help--- - Faraday FOTG210 is an OTG controller which can be configured as - an USB2.0 host. It is designed to meet USB2.0 EHCI specification - with minor modification. - - To compile this driver as a module, choose M here: the - module will be called fotg210-hcd. - config USB_OHCI_HCD tristate "OHCI HCD (USB 1.1) support" select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 829a3397882a..bea71127b15f 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -58,4 +58,3 @@ obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o obj-$(CONFIG_USB_FUSBH200_HCD) += fusbh200-hcd.o -obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c deleted file mode 100644 index fce13bcc4a3e..000000000000 --- a/drivers/usb/host/fotg210-hcd.c +++ /dev/null @@ -1,6049 +0,0 @@ -/* - * Faraday FOTG210 EHCI-like driver - * - * Copyright (c) 2013 Faraday Technology Corporation - * - * Author: Yuan-Hsin Chen - * Feng-Hsin Chiang - * Po-Yu Chuang - * - * Most of code borrowed from the Linux-3.7 EHCI driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/*-------------------------------------------------------------------------*/ -#define DRIVER_AUTHOR "Yuan-Hsin Chen" -#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" - -static const char hcd_name[] = "fotg210_hcd"; - -#undef VERBOSE_DEBUG -#undef FOTG210_URB_TRACE - -#ifdef DEBUG -#define FOTG210_STATS -#endif - -/* magic numbers that can affect system performance */ -#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ -#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ -#define FOTG210_TUNE_RL_TT 0 -#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ -#define FOTG210_TUNE_MULT_TT 1 -/* - * Some drivers think it's safe to schedule isochronous transfers more than - * 256 ms into the future (partly as a result of an old bug in the scheduling - * code). In an attempt to avoid trouble, we will use a minimum scheduling - * length of 512 frames instead of 256. - */ -#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ - -/* Initial IRQ latency: faster than hw default */ -static int log2_irq_thresh; /* 0 to 6 */ -module_param(log2_irq_thresh, int, S_IRUGO); -MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); - -/* initial park setting: slower than hw default */ -static unsigned park; -module_param(park, uint, S_IRUGO); -MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); - -/* for link power management(LPM) feature */ -static unsigned int hird; -module_param(hird, int, S_IRUGO); -MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); - -#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) - -#include "fotg210.h" - -/*-------------------------------------------------------------------------*/ - -#define fotg210_dbg(fotg210, fmt, args...) \ - dev_dbg(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) -#define fotg210_err(fotg210, fmt, args...) \ - dev_err(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) -#define fotg210_info(fotg210, fmt, args...) \ - dev_info(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) -#define fotg210_warn(fotg210, fmt, args...) \ - dev_warn(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) - -#ifdef VERBOSE_DEBUG -# define fotg210_vdbg fotg210_dbg -#else - static inline void fotg210_vdbg(struct fotg210_hcd *fotg210, ...) {} -#endif - -#ifdef DEBUG - -/* check the values in the HCSPARAMS register - * (host controller _Structural_ parameters) - * see EHCI spec, Table 2-4 for each value - */ -static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) -{ - u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); - - fotg210_dbg(fotg210, - "%s hcs_params 0x%x ports=%d\n", - label, params, - HCS_N_PORTS(params) - ); -} -#else - -static inline void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) {} - -#endif - -#ifdef DEBUG - -/* check the values in the HCCPARAMS register - * (host controller _Capability_ parameters) - * see EHCI Spec, Table 2-5 for each value - * */ -static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) -{ - u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); - - fotg210_dbg(fotg210, - "%s hcc_params %04x uframes %s%s\n", - label, - params, - HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", - HCC_CANPARK(params) ? " park" : ""); -} -#else - -static inline void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) {} - -#endif - -#ifdef DEBUG - -static void __maybe_unused -dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) -{ - fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, - hc32_to_cpup(fotg210, &qtd->hw_next), - hc32_to_cpup(fotg210, &qtd->hw_alt_next), - hc32_to_cpup(fotg210, &qtd->hw_token), - hc32_to_cpup(fotg210, &qtd->hw_buf[0])); - if (qtd->hw_buf[1]) - fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", - hc32_to_cpup(fotg210, &qtd->hw_buf[1]), - hc32_to_cpup(fotg210, &qtd->hw_buf[2]), - hc32_to_cpup(fotg210, &qtd->hw_buf[3]), - hc32_to_cpup(fotg210, &qtd->hw_buf[4])); -} - -static void __maybe_unused -dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{ - struct fotg210_qh_hw *hw = qh->hw; - - fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, - qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current); - dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); -} - -static void __maybe_unused -dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) -{ - fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", - label, itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), - itd->urb); - fotg210_dbg(fotg210, - " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", - hc32_to_cpu(fotg210, itd->hw_transaction[0]), - hc32_to_cpu(fotg210, itd->hw_transaction[1]), - hc32_to_cpu(fotg210, itd->hw_transaction[2]), - hc32_to_cpu(fotg210, itd->hw_transaction[3]), - hc32_to_cpu(fotg210, itd->hw_transaction[4]), - hc32_to_cpu(fotg210, itd->hw_transaction[5]), - hc32_to_cpu(fotg210, itd->hw_transaction[6]), - hc32_to_cpu(fotg210, itd->hw_transaction[7])); - fotg210_dbg(fotg210, - " buf: %08x %08x %08x %08x %08x %08x %08x\n", - hc32_to_cpu(fotg210, itd->hw_bufp[0]), - hc32_to_cpu(fotg210, itd->hw_bufp[1]), - hc32_to_cpu(fotg210, itd->hw_bufp[2]), - hc32_to_cpu(fotg210, itd->hw_bufp[3]), - hc32_to_cpu(fotg210, itd->hw_bufp[4]), - hc32_to_cpu(fotg210, itd->hw_bufp[5]), - hc32_to_cpu(fotg210, itd->hw_bufp[6])); - fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", - itd->index[0], itd->index[1], itd->index[2], - itd->index[3], itd->index[4], itd->index[5], - itd->index[6], itd->index[7]); -} - -static int __maybe_unused -dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) -{ - return scnprintf(buf, len, - "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", - label, label[0] ? " " : "", status, - (status & STS_ASS) ? " Async" : "", - (status & STS_PSS) ? " Periodic" : "", - (status & STS_RECL) ? " Recl" : "", - (status & STS_HALT) ? " Halt" : "", - (status & STS_IAA) ? " IAA" : "", - (status & STS_FATAL) ? " FATAL" : "", - (status & STS_FLR) ? " FLR" : "", - (status & STS_PCD) ? " PCD" : "", - (status & STS_ERR) ? " ERR" : "", - (status & STS_INT) ? " INT" : "" - ); -} - -static int __maybe_unused -dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) -{ - return scnprintf(buf, len, - "%s%sintrenable %02x%s%s%s%s%s%s", - label, label[0] ? " " : "", enable, - (enable & STS_IAA) ? " IAA" : "", - (enable & STS_FATAL) ? " FATAL" : "", - (enable & STS_FLR) ? " FLR" : "", - (enable & STS_PCD) ? " PCD" : "", - (enable & STS_ERR) ? " ERR" : "", - (enable & STS_INT) ? " INT" : "" - ); -} - -static const char *const fls_strings[] = { "1024", "512", "256", "??" }; - -static int -dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) -{ - return scnprintf(buf, len, - "%s%scommand %07x %s=%d ithresh=%d%s%s%s " - "period=%s%s %s", - label, label[0] ? " " : "", command, - (command & CMD_PARK) ? " park" : "(park)", - CMD_PARK_CNT(command), - (command >> 16) & 0x3f, - (command & CMD_IAAD) ? " IAAD" : "", - (command & CMD_ASE) ? " Async" : "", - (command & CMD_PSE) ? " Periodic" : "", - fls_strings[(command >> 2) & 0x3], - (command & CMD_RESET) ? " Reset" : "", - (command & CMD_RUN) ? "RUN" : "HALT" - ); -} - -static int -dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) -{ - char *sig; - - /* signaling state */ - switch (status & (3 << 10)) { - case 0 << 10: - sig = "se0"; - break; - case 1 << 10: - sig = "k"; - break; /* low speed */ - case 2 << 10: - sig = "j"; - break; - default: - sig = "?"; - break; - } - - return scnprintf(buf, len, - "%s%sport:%d status %06x %d " - "sig=%s%s%s%s%s%s%s%s", - label, label[0] ? " " : "", port, status, - status>>25,/*device address */ - sig, - (status & PORT_RESET) ? " RESET" : "", - (status & PORT_SUSPEND) ? " SUSPEND" : "", - (status & PORT_RESUME) ? " RESUME" : "", - (status & PORT_PEC) ? " PEC" : "", - (status & PORT_PE) ? " PE" : "", - (status & PORT_CSC) ? " CSC" : "", - (status & PORT_CONNECT) ? " CONNECT" : ""); -} - -#else -static inline void __maybe_unused -dbg_qh(char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{} - -static inline int __maybe_unused -dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) -{ return 0; } - -static inline int __maybe_unused -dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) -{ return 0; } - -static inline int __maybe_unused -dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) -{ return 0; } - -static inline int __maybe_unused -dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) -{ return 0; } - -#endif /* DEBUG */ - -/* functions have the "wrong" filename when they're output... */ -#define dbg_status(fotg210, label, status) { \ - char _buf[80]; \ - dbg_status_buf(_buf, sizeof(_buf), label, status); \ - fotg210_dbg(fotg210, "%s\n", _buf); \ -} - -#define dbg_cmd(fotg210, label, command) { \ - char _buf[80]; \ - dbg_command_buf(_buf, sizeof(_buf), label, command); \ - fotg210_dbg(fotg210, "%s\n", _buf); \ -} - -#define dbg_port(fotg210, label, port, status) { \ - char _buf[80]; \ - dbg_port_buf(_buf, sizeof(_buf), label, port, status); \ - fotg210_dbg(fotg210, "%s\n", _buf); \ -} - -/*-------------------------------------------------------------------------*/ - -#ifdef STUB_DEBUG_FILES - -static inline void create_debug_files(struct fotg210_hcd *bus) { } -static inline void remove_debug_files(struct fotg210_hcd *bus) { } - -#else - -/* troubleshooting help: expose state in debugfs */ - -static int debug_async_open(struct inode *, struct file *); -static int debug_periodic_open(struct inode *, struct file *); -static int debug_registers_open(struct inode *, struct file *); -static int debug_async_open(struct inode *, struct file *); - -static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); -static int debug_close(struct inode *, struct file *); - -static const struct file_operations debug_async_fops = { - .owner = THIS_MODULE, - .open = debug_async_open, - .read = debug_output, - .release = debug_close, - .llseek = default_llseek, -}; -static const struct file_operations debug_periodic_fops = { - .owner = THIS_MODULE, - .open = debug_periodic_open, - .read = debug_output, - .release = debug_close, - .llseek = default_llseek, -}; -static const struct file_operations debug_registers_fops = { - .owner = THIS_MODULE, - .open = debug_registers_open, - .read = debug_output, - .release = debug_close, - .llseek = default_llseek, -}; - -static struct dentry *fotg210_debug_root; - -struct debug_buffer { - ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ - struct usb_bus *bus; - struct mutex mutex; /* protect filling of buffer */ - size_t count; /* number of characters filled into buffer */ - char *output_buf; - size_t alloc_size; -}; - -#define speed_char(info1)({ char tmp; \ - switch (info1 & (3 << 12)) { \ - case QH_FULL_SPEED: \ - tmp = 'f'; break; \ - case QH_LOW_SPEED: \ - tmp = 'l'; break; \ - case QH_HIGH_SPEED: \ - tmp = 'h'; break; \ - default: \ - tmp = '?'; break; \ - }; tmp; }) - -static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) -{ - __u32 v = hc32_to_cpu(fotg210, token); - - if (v & QTD_STS_ACTIVE) - return '*'; - if (v & QTD_STS_HALT) - return '-'; - if (!IS_SHORT_READ(v)) - return ' '; - /* tries to advance through hw_alt_next */ - return '/'; -} - -static void qh_lines( - struct fotg210_hcd *fotg210, - struct fotg210_qh *qh, - char **nextp, - unsigned *sizep -) -{ - u32 scratch; - u32 hw_curr; - struct fotg210_qtd *td; - unsigned temp; - unsigned size = *sizep; - char *next = *nextp; - char mark; - __le32 list_end = FOTG210_LIST_END(fotg210); - struct fotg210_qh_hw *hw = qh->hw; - - if (hw->hw_qtd_next == list_end) /* NEC does this */ - mark = '@'; - else - mark = token_mark(fotg210, hw->hw_token); - if (mark == '/') { /* qh_alt_next controls qh advance? */ - if ((hw->hw_alt_next & QTD_MASK(fotg210)) - == fotg210->async->hw->hw_alt_next) - mark = '#'; /* blocked */ - else if (hw->hw_alt_next == list_end) - mark = '.'; /* use hw_qtd_next */ - /* else alt_next points to some other qtd */ - } - scratch = hc32_to_cpup(fotg210, &hw->hw_info1); - hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0; - temp = scnprintf(next, size, - "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)", - qh, scratch & 0x007f, - speed_char(scratch), - (scratch >> 8) & 0x000f, - scratch, hc32_to_cpup(fotg210, &hw->hw_info2), - hc32_to_cpup(fotg210, &hw->hw_token), mark, - (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token) - ? "data1" : "data0", - (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f); - size -= temp; - next += temp; - - /* hc may be modifying the list as we read it ... */ - list_for_each_entry(td, &qh->qtd_list, qtd_list) { - scratch = hc32_to_cpup(fotg210, &td->hw_token); - mark = ' '; - if (hw_curr == td->qtd_dma) - mark = '*'; - else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma)) - mark = '+'; - else if (QTD_LENGTH(scratch)) { - if (td->hw_alt_next == fotg210->async->hw->hw_alt_next) - mark = '#'; - else if (td->hw_alt_next != list_end) - mark = '/'; - } - temp = snprintf(next, size, - "\n\t%p%c%s len=%d %08x urb %p", - td, mark, ({ char *tmp; - switch ((scratch>>8)&0x03) { - case 0: - tmp = "out"; - break; - case 1: - tmp = "in"; - break; - case 2: - tmp = "setup"; - break; - default: - tmp = "?"; - break; - } tmp; }), - (scratch >> 16) & 0x7fff, - scratch, - td->urb); - if (size < temp) - temp = size; - size -= temp; - next += temp; - if (temp == size) - goto done; - } - - temp = snprintf(next, size, "\n"); - if (size < temp) - temp = size; - size -= temp; - next += temp; - -done: - *sizep = size; - *nextp = next; -} - -static ssize_t fill_async_buffer(struct debug_buffer *buf) -{ - struct usb_hcd *hcd; - struct fotg210_hcd *fotg210; - unsigned long flags; - unsigned temp, size; - char *next; - struct fotg210_qh *qh; - - hcd = bus_to_hcd(buf->bus); - fotg210 = hcd_to_fotg210(hcd); - next = buf->output_buf; - size = buf->alloc_size; - - *next = 0; - - /* dumps a snapshot of the async schedule. - * usually empty except for long-term bulk reads, or head. - * one QH per line, and TDs we know about - */ - spin_lock_irqsave(&fotg210->lock, flags); - for (qh = fotg210->async->qh_next.qh; size > 0 && qh; - qh = qh->qh_next.qh) - qh_lines(fotg210, qh, &next, &size); - if (fotg210->async_unlink && size > 0) { - temp = scnprintf(next, size, "\nunlink =\n"); - size -= temp; - next += temp; - - for (qh = fotg210->async_unlink; size > 0 && qh; - qh = qh->unlink_next) - qh_lines(fotg210, qh, &next, &size); - } - spin_unlock_irqrestore(&fotg210->lock, flags); - - return strlen(buf->output_buf); -} - -#define DBG_SCHED_LIMIT 64 -static ssize_t fill_periodic_buffer(struct debug_buffer *buf) -{ - struct usb_hcd *hcd; - struct fotg210_hcd *fotg210; - unsigned long flags; - union fotg210_shadow p, *seen; - unsigned temp, size, seen_count; - char *next; - unsigned i; - __hc32 tag; - - seen = kmalloc(DBG_SCHED_LIMIT * sizeof(*seen), GFP_ATOMIC); - if (!seen) - return 0; - seen_count = 0; - - hcd = bus_to_hcd(buf->bus); - fotg210 = hcd_to_fotg210(hcd); - next = buf->output_buf; - size = buf->alloc_size; - - temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size); - size -= temp; - next += temp; - - /* dump a snapshot of the periodic schedule. - * iso changes, interrupt usually doesn't. - */ - spin_lock_irqsave(&fotg210->lock, flags); - for (i = 0; i < fotg210->periodic_size; i++) { - p = fotg210->pshadow[i]; - if (likely(!p.ptr)) - continue; - tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]); - - temp = scnprintf(next, size, "%4d: ", i); - size -= temp; - next += temp; - - do { - struct fotg210_qh_hw *hw; - - switch (hc32_to_cpu(fotg210, tag)) { - case Q_TYPE_QH: - hw = p.qh->hw; - temp = scnprintf(next, size, " qh%d-%04x/%p", - p.qh->period, - hc32_to_cpup(fotg210, - &hw->hw_info2) - /* uframe masks */ - & (QH_CMASK | QH_SMASK), - p.qh); - size -= temp; - next += temp; - /* don't repeat what follows this qh */ - for (temp = 0; temp < seen_count; temp++) { - if (seen[temp].ptr != p.ptr) - continue; - if (p.qh->qh_next.ptr) { - temp = scnprintf(next, size, - " ..."); - size -= temp; - next += temp; - } - break; - } - /* show more info the first time around */ - if (temp == seen_count) { - u32 scratch = hc32_to_cpup(fotg210, - &hw->hw_info1); - struct fotg210_qtd *qtd; - char *type = ""; - - /* count tds, get ep direction */ - temp = 0; - list_for_each_entry(qtd, - &p.qh->qtd_list, - qtd_list) { - temp++; - switch (0x03 & (hc32_to_cpu( - fotg210, - qtd->hw_token) >> 8)) { - case 0: - type = "out"; - continue; - case 1: - type = "in"; - continue; - } - } - - temp = scnprintf(next, size, - "(%c%d ep%d%s " - "[%d/%d] q%d p%d)", - speed_char(scratch), - scratch & 0x007f, - (scratch >> 8) & 0x000f, type, - p.qh->usecs, p.qh->c_usecs, - temp, - 0x7ff & (scratch >> 16)); - - if (seen_count < DBG_SCHED_LIMIT) - seen[seen_count++].qh = p.qh; - } else - temp = 0; - tag = Q_NEXT_TYPE(fotg210, hw->hw_next); - p = p.qh->qh_next; - break; - case Q_TYPE_FSTN: - temp = scnprintf(next, size, - " fstn-%8x/%p", p.fstn->hw_prev, - p.fstn); - tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next); - p = p.fstn->fstn_next; - break; - case Q_TYPE_ITD: - temp = scnprintf(next, size, - " itd/%p", p.itd); - tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); - p = p.itd->itd_next; - break; - } - size -= temp; - next += temp; - } while (p.ptr); - - temp = scnprintf(next, size, "\n"); - size -= temp; - next += temp; - } - spin_unlock_irqrestore(&fotg210->lock, flags); - kfree(seen); - - return buf->alloc_size - size; -} -#undef DBG_SCHED_LIMIT - -static const char *rh_state_string(struct fotg210_hcd *fotg210) -{ - switch (fotg210->rh_state) { - case FOTG210_RH_HALTED: - return "halted"; - case FOTG210_RH_SUSPENDED: - return "suspended"; - case FOTG210_RH_RUNNING: - return "running"; - case FOTG210_RH_STOPPING: - return "stopping"; - } - return "?"; -} - -static ssize_t fill_registers_buffer(struct debug_buffer *buf) -{ - struct usb_hcd *hcd; - struct fotg210_hcd *fotg210; - unsigned long flags; - unsigned temp, size, i; - char *next, scratch[80]; - static const char fmt[] = "%*s\n"; - static const char label[] = ""; - - hcd = bus_to_hcd(buf->bus); - fotg210 = hcd_to_fotg210(hcd); - next = buf->output_buf; - size = buf->alloc_size; - - spin_lock_irqsave(&fotg210->lock, flags); - - if (!HCD_HW_ACCESSIBLE(hcd)) { - size = scnprintf(next, size, - "bus %s, device %s\n" - "%s\n" - "SUSPENDED(no register access)\n", - hcd->self.controller->bus->name, - dev_name(hcd->self.controller), - hcd->product_desc); - goto done; - } - - /* Capability Registers */ - i = HC_VERSION(fotg210, fotg210_readl(fotg210, - &fotg210->caps->hc_capbase)); - temp = scnprintf(next, size, - "bus %s, device %s\n" - "%s\n" - "EHCI %x.%02x, rh state %s\n", - hcd->self.controller->bus->name, - dev_name(hcd->self.controller), - hcd->product_desc, - i >> 8, i & 0x0ff, rh_state_string(fotg210)); - size -= temp; - next += temp; - - /* FIXME interpret both types of params */ - i = fotg210_readl(fotg210, &fotg210->caps->hcs_params); - temp = scnprintf(next, size, "structural params 0x%08x\n", i); - size -= temp; - next += temp; - - i = fotg210_readl(fotg210, &fotg210->caps->hcc_params); - temp = scnprintf(next, size, "capability params 0x%08x\n", i); - size -= temp; - next += temp; - - /* Operational Registers */ - temp = dbg_status_buf(scratch, sizeof(scratch), label, - fotg210_readl(fotg210, &fotg210->regs->status)); - temp = scnprintf(next, size, fmt, temp, scratch); - size -= temp; - next += temp; - - temp = dbg_command_buf(scratch, sizeof(scratch), label, - fotg210_readl(fotg210, &fotg210->regs->command)); - temp = scnprintf(next, size, fmt, temp, scratch); - size -= temp; - next += temp; - - temp = dbg_intr_buf(scratch, sizeof(scratch), label, - fotg210_readl(fotg210, &fotg210->regs->intr_enable)); - temp = scnprintf(next, size, fmt, temp, scratch); - size -= temp; - next += temp; - - temp = scnprintf(next, size, "uframe %04x\n", - fotg210_read_frame_index(fotg210)); - size -= temp; - next += temp; - - if (fotg210->async_unlink) { - temp = scnprintf(next, size, "async unlink qh %p\n", - fotg210->async_unlink); - size -= temp; - next += temp; - } - -#ifdef FOTG210_STATS - temp = scnprintf(next, size, - "irq normal %ld err %ld iaa %ld(lost %ld)\n", - fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa, - fotg210->stats.lost_iaa); - size -= temp; - next += temp; - - temp = scnprintf(next, size, "complete %ld unlink %ld\n", - fotg210->stats.complete, fotg210->stats.unlink); - size -= temp; - next += temp; -#endif - -done: - spin_unlock_irqrestore(&fotg210->lock, flags); - - return buf->alloc_size - size; -} - -static struct debug_buffer *alloc_buffer(struct usb_bus *bus, - ssize_t (*fill_func)(struct debug_buffer *)) -{ - struct debug_buffer *buf; - - buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); - - if (buf) { - buf->bus = bus; - buf->fill_func = fill_func; - mutex_init(&buf->mutex); - buf->alloc_size = PAGE_SIZE; - } - - return buf; -} - -static int fill_buffer(struct debug_buffer *buf) -{ - int ret = 0; - - if (!buf->output_buf) - buf->output_buf = vmalloc(buf->alloc_size); - - if (!buf->output_buf) { - ret = -ENOMEM; - goto out; - } - - ret = buf->fill_func(buf); - - if (ret >= 0) { - buf->count = ret; - ret = 0; - } - -out: - return ret; -} - -static ssize_t debug_output(struct file *file, char __user *user_buf, - size_t len, loff_t *offset) -{ - struct debug_buffer *buf = file->private_data; - int ret = 0; - - mutex_lock(&buf->mutex); - if (buf->count == 0) { - ret = fill_buffer(buf); - if (ret != 0) { - mutex_unlock(&buf->mutex); - goto out; - } - } - mutex_unlock(&buf->mutex); - - ret = simple_read_from_buffer(user_buf, len, offset, - buf->output_buf, buf->count); - -out: - return ret; - -} - -static int debug_close(struct inode *inode, struct file *file) -{ - struct debug_buffer *buf = file->private_data; - - if (buf) { - vfree(buf->output_buf); - kfree(buf); - } - - return 0; -} -static int debug_async_open(struct inode *inode, struct file *file) -{ - file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); - - return file->private_data ? 0 : -ENOMEM; -} - -static int debug_periodic_open(struct inode *inode, struct file *file) -{ - struct debug_buffer *buf; - buf = alloc_buffer(inode->i_private, fill_periodic_buffer); - if (!buf) - return -ENOMEM; - - buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; - file->private_data = buf; - return 0; -} - -static int debug_registers_open(struct inode *inode, struct file *file) -{ - file->private_data = alloc_buffer(inode->i_private, - fill_registers_buffer); - - return file->private_data ? 0 : -ENOMEM; -} - -static inline void create_debug_files(struct fotg210_hcd *fotg210) -{ - struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; - - fotg210->debug_dir = debugfs_create_dir(bus->bus_name, - fotg210_debug_root); - if (!fotg210->debug_dir) - return; - - if (!debugfs_create_file("async", S_IRUGO, fotg210->debug_dir, bus, - &debug_async_fops)) - goto file_error; - - if (!debugfs_create_file("periodic", S_IRUGO, fotg210->debug_dir, bus, - &debug_periodic_fops)) - goto file_error; - - if (!debugfs_create_file("registers", S_IRUGO, fotg210->debug_dir, bus, - &debug_registers_fops)) - goto file_error; - - return; - -file_error: - debugfs_remove_recursive(fotg210->debug_dir); -} - -static inline void remove_debug_files(struct fotg210_hcd *fotg210) -{ - debugfs_remove_recursive(fotg210->debug_dir); -} - -#endif /* STUB_DEBUG_FILES */ -/*-------------------------------------------------------------------------*/ - -/* - * handshake - spin reading hc until handshake completes or fails - * @ptr: address of hc register to be read - * @mask: bits to look at in result of read - * @done: value of those bits when handshake succeeds - * @usec: timeout in microseconds - * - * Returns negative errno, or zero on success - * - * Success happens when the "mask" bits have the specified value (hardware - * handshake done). There are two failure modes: "usec" have passed (major - * hardware flakeout), or the register reads as all-ones (hardware removed). - * - * That last failure should_only happen in cases like physical cardbus eject - * before driver shutdown. But it also seems to be caused by bugs in cardbus - * bridge shutdown: shutting down the bridge before the devices using it. - */ -static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, - u32 mask, u32 done, int usec) -{ - u32 result; - - do { - result = fotg210_readl(fotg210, ptr); - if (result == ~(u32)0) /* card removed */ - return -ENODEV; - result &= mask; - if (result == done) - return 0; - udelay(1); - usec--; - } while (usec > 0); - return -ETIMEDOUT; -} - -/* - * Force HC to halt state from unknown (EHCI spec section 2.3). - * Must be called with interrupts enabled and the lock not held. - */ -static int fotg210_halt(struct fotg210_hcd *fotg210) -{ - u32 temp; - - spin_lock_irq(&fotg210->lock); - - /* disable any irqs left enabled by previous code */ - fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); - - /* - * This routine gets called during probe before fotg210->command - * has been initialized, so we can't rely on its value. - */ - fotg210->command &= ~CMD_RUN; - temp = fotg210_readl(fotg210, &fotg210->regs->command); - temp &= ~(CMD_RUN | CMD_IAAD); - fotg210_writel(fotg210, temp, &fotg210->regs->command); - - spin_unlock_irq(&fotg210->lock); - synchronize_irq(fotg210_to_hcd(fotg210)->irq); - - return handshake(fotg210, &fotg210->regs->status, - STS_HALT, STS_HALT, 16 * 125); -} - -/* - * Reset a non-running (STS_HALT == 1) controller. - * Must be called with interrupts enabled and the lock not held. - */ -static int fotg210_reset(struct fotg210_hcd *fotg210) -{ - int retval; - u32 command = fotg210_readl(fotg210, &fotg210->regs->command); - - /* If the EHCI debug controller is active, special care must be - * taken before and after a host controller reset */ - if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210))) - fotg210->debug = NULL; - - command |= CMD_RESET; - dbg_cmd(fotg210, "reset", command); - fotg210_writel(fotg210, command, &fotg210->regs->command); - fotg210->rh_state = FOTG210_RH_HALTED; - fotg210->next_statechange = jiffies; - retval = handshake(fotg210, &fotg210->regs->command, - CMD_RESET, 0, 250 * 1000); - - if (retval) - return retval; - - if (fotg210->debug) - dbgp_external_startup(fotg210_to_hcd(fotg210)); - - fotg210->port_c_suspend = fotg210->suspended_ports = - fotg210->resuming_ports = 0; - return retval; -} - -/* - * Idle the controller (turn off the schedules). - * Must be called with interrupts enabled and the lock not held. - */ -static void fotg210_quiesce(struct fotg210_hcd *fotg210) -{ - u32 temp; - - if (fotg210->rh_state != FOTG210_RH_RUNNING) - return; - - /* wait for any schedule enables/disables to take effect */ - temp = (fotg210->command << 10) & (STS_ASS | STS_PSS); - handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp, - 16 * 125); - - /* then disable anything that's still active */ - spin_lock_irq(&fotg210->lock); - fotg210->command &= ~(CMD_ASE | CMD_PSE); - fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); - spin_unlock_irq(&fotg210->lock); - - /* hardware can take 16 microframes to turn off ... */ - handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0, - 16 * 125); -} - -/*-------------------------------------------------------------------------*/ - -static void end_unlink_async(struct fotg210_hcd *fotg210); -static void unlink_empty_async(struct fotg210_hcd *fotg210); -static void fotg210_work(struct fotg210_hcd *fotg210); -static void start_unlink_intr(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh); -static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); - -/*-------------------------------------------------------------------------*/ - -/* Set a bit in the USBCMD register */ -static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) -{ - fotg210->command |= bit; - fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); - - /* unblock posted write */ - fotg210_readl(fotg210, &fotg210->regs->command); -} - -/* Clear a bit in the USBCMD register */ -static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) -{ - fotg210->command &= ~bit; - fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); - - /* unblock posted write */ - fotg210_readl(fotg210, &fotg210->regs->command); -} - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI timer support... Now using hrtimers. - * - * Lots of different events are triggered from fotg210->hrtimer. Whenever - * the timer routine runs, it checks each possible event; events that are - * currently enabled and whose expiration time has passed get handled. - * The set of enabled events is stored as a collection of bitflags in - * fotg210->enabled_hrtimer_events, and they are numbered in order of - * increasing delay values (ranging between 1 ms and 100 ms). - * - * Rather than implementing a sorted list or tree of all pending events, - * we keep track only of the lowest-numbered pending event, in - * fotg210->next_hrtimer_event. Whenever fotg210->hrtimer gets restarted, its - * expiration time is set to the timeout value for this event. - * - * As a result, events might not get handled right away; the actual delay - * could be anywhere up to twice the requested delay. This doesn't - * matter, because none of the events are especially time-critical. The - * ones that matter most all have a delay of 1 ms, so they will be - * handled after 2 ms at most, which is okay. In addition to this, we - * allow for an expiration range of 1 ms. - */ - -/* - * Delay lengths for the hrtimer event types. - * Keep this list sorted by delay length, in the same order as - * the event types indexed by enum fotg210_hrtimer_event in fotg210.h. - */ -static unsigned event_delays_ns[] = { - 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */ - 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */ - 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */ - 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */ - 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */ - 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ - 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */ - 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ - 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */ - 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */ -}; - -/* Enable a pending hrtimer event */ -static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, - bool resched) -{ - ktime_t *timeout = &fotg210->hr_timeouts[event]; - - if (resched) - *timeout = ktime_add(ktime_get(), - ktime_set(0, event_delays_ns[event])); - fotg210->enabled_hrtimer_events |= (1 << event); - - /* Track only the lowest-numbered pending event */ - if (event < fotg210->next_hrtimer_event) { - fotg210->next_hrtimer_event = event; - hrtimer_start_range_ns(&fotg210->hrtimer, *timeout, - NSEC_PER_MSEC, HRTIMER_MODE_ABS); - } -} - - -/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ -static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) -{ - unsigned actual, want; - - /* Don't enable anything if the controller isn't running (e.g., died) */ - if (fotg210->rh_state != FOTG210_RH_RUNNING) - return; - - want = (fotg210->command & CMD_ASE) ? STS_ASS : 0; - actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS; - - if (want != actual) { - - /* Poll again later, but give up after about 20 ms */ - if (fotg210->ASS_poll_count++ < 20) { - fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS, - true); - return; - } - fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n", - want, actual); - } - fotg210->ASS_poll_count = 0; - - /* The status is up-to-date; restart or stop the schedule as needed */ - if (want == 0) { /* Stopped */ - if (fotg210->async_count > 0) - fotg210_set_command_bit(fotg210, CMD_ASE); - - } else { /* Running */ - if (fotg210->async_count == 0) { - - /* Turn off the schedule after a while */ - fotg210_enable_event(fotg210, - FOTG210_HRTIMER_DISABLE_ASYNC, - true); - } - } -} - -/* Turn off the async schedule after a brief delay */ -static void fotg210_disable_ASE(struct fotg210_hcd *fotg210) -{ - fotg210_clear_command_bit(fotg210, CMD_ASE); -} - - -/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ -static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) -{ - unsigned actual, want; - - /* Don't do anything if the controller isn't running (e.g., died) */ - if (fotg210->rh_state != FOTG210_RH_RUNNING) - return; - - want = (fotg210->command & CMD_PSE) ? STS_PSS : 0; - actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS; - - if (want != actual) { - - /* Poll again later, but give up after about 20 ms */ - if (fotg210->PSS_poll_count++ < 20) { - fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS, - true); - return; - } - fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n", - want, actual); - } - fotg210->PSS_poll_count = 0; - - /* The status is up-to-date; restart or stop the schedule as needed */ - if (want == 0) { /* Stopped */ - if (fotg210->periodic_count > 0) - fotg210_set_command_bit(fotg210, CMD_PSE); - - } else { /* Running */ - if (fotg210->periodic_count == 0) { - - /* Turn off the schedule after a while */ - fotg210_enable_event(fotg210, - FOTG210_HRTIMER_DISABLE_PERIODIC, - true); - } - } -} - -/* Turn off the periodic schedule after a brief delay */ -static void fotg210_disable_PSE(struct fotg210_hcd *fotg210) -{ - fotg210_clear_command_bit(fotg210, CMD_PSE); -} - - -/* Poll the STS_HALT status bit; see when a dead controller stops */ -static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) -{ - if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) { - - /* Give up after a few milliseconds */ - if (fotg210->died_poll_count++ < 5) { - /* Try again later */ - fotg210_enable_event(fotg210, - FOTG210_HRTIMER_POLL_DEAD, true); - return; - } - fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n"); - } - - /* Clean up the mess */ - fotg210->rh_state = FOTG210_RH_HALTED; - fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); - fotg210_work(fotg210); - end_unlink_async(fotg210); - - /* Not in process context, so don't try to reset the controller */ -} - - -/* Handle unlinked interrupt QHs once they are gone from the hardware */ -static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) -{ - bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); - - /* - * Process all the QHs on the intr_unlink list that were added - * before the current unlink cycle began. The list is in - * temporal order, so stop when we reach the first entry in the - * current cycle. But if the root hub isn't running then - * process all the QHs on the list. - */ - fotg210->intr_unlinking = true; - while (fotg210->intr_unlink) { - struct fotg210_qh *qh = fotg210->intr_unlink; - - if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle) - break; - fotg210->intr_unlink = qh->unlink_next; - qh->unlink_next = NULL; - end_unlink_intr(fotg210, qh); - } - - /* Handle remaining entries later */ - if (fotg210->intr_unlink) { - fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, - true); - ++fotg210->intr_unlink_cycle; - } - fotg210->intr_unlinking = false; -} - - -/* Start another free-iTDs/siTDs cycle */ -static void start_free_itds(struct fotg210_hcd *fotg210) -{ - if (!(fotg210->enabled_hrtimer_events & - BIT(FOTG210_HRTIMER_FREE_ITDS))) { - fotg210->last_itd_to_free = list_entry( - fotg210->cached_itd_list.prev, - struct fotg210_itd, itd_list); - fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true); - } -} - -/* Wait for controller to stop using old iTDs and siTDs */ -static void end_free_itds(struct fotg210_hcd *fotg210) -{ - struct fotg210_itd *itd, *n; - - if (fotg210->rh_state < FOTG210_RH_RUNNING) - fotg210->last_itd_to_free = NULL; - - list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) { - list_del(&itd->itd_list); - dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma); - if (itd == fotg210->last_itd_to_free) - break; - } - - if (!list_empty(&fotg210->cached_itd_list)) - start_free_itds(fotg210); -} - - -/* Handle lost (or very late) IAA interrupts */ -static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) -{ - if (fotg210->rh_state != FOTG210_RH_RUNNING) - return; - - /* - * Lost IAA irqs wedge things badly; seen first with a vt8235. - * So we need this watchdog, but must protect it against both - * (a) SMP races against real IAA firing and retriggering, and - * (b) clean HC shutdown, when IAA watchdog was pending. - */ - if (fotg210->async_iaa) { - u32 cmd, status; - - /* If we get here, IAA is *REALLY* late. It's barely - * conceivable that the system is so busy that CMD_IAAD - * is still legitimately set, so let's be sure it's - * clear before we read STS_IAA. (The HC should clear - * CMD_IAAD when it sets STS_IAA.) - */ - cmd = fotg210_readl(fotg210, &fotg210->regs->command); - - /* - * If IAA is set here it either legitimately triggered - * after the watchdog timer expired (_way_ late, so we'll - * still count it as lost) ... or a silicon erratum: - * - VIA seems to set IAA without triggering the IRQ; - * - IAAD potentially cleared without setting IAA. - */ - status = fotg210_readl(fotg210, &fotg210->regs->status); - if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { - COUNT(fotg210->stats.lost_iaa); - fotg210_writel(fotg210, STS_IAA, - &fotg210->regs->status); - } - - fotg210_vdbg(fotg210, "IAA watchdog: status %x cmd %x\n", - status, cmd); - end_unlink_async(fotg210); - } -} - - -/* Enable the I/O watchdog, if appropriate */ -static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) -{ - /* Not needed if the controller isn't running or it's already enabled */ - if (fotg210->rh_state != FOTG210_RH_RUNNING || - (fotg210->enabled_hrtimer_events & - BIT(FOTG210_HRTIMER_IO_WATCHDOG))) - return; - - /* - * Isochronous transfers always need the watchdog. - * For other sorts we use it only if the flag is set. - */ - if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog && - fotg210->async_count + fotg210->intr_count > 0)) - fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG, - true); -} - - -/* - * Handler functions for the hrtimer event types. - * Keep this array in the same order as the event types indexed by - * enum fotg210_hrtimer_event in fotg210.h. - */ -static void (*event_handlers[])(struct fotg210_hcd *) = { - fotg210_poll_ASS, /* FOTG210_HRTIMER_POLL_ASS */ - fotg210_poll_PSS, /* FOTG210_HRTIMER_POLL_PSS */ - fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */ - fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */ - end_free_itds, /* FOTG210_HRTIMER_FREE_ITDS */ - unlink_empty_async, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ - fotg210_iaa_watchdog, /* FOTG210_HRTIMER_IAA_WATCHDOG */ - fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ - fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */ - fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */ -}; - -static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) -{ - struct fotg210_hcd *fotg210 = - container_of(t, struct fotg210_hcd, hrtimer); - ktime_t now; - unsigned long events; - unsigned long flags; - unsigned e; - - spin_lock_irqsave(&fotg210->lock, flags); - - events = fotg210->enabled_hrtimer_events; - fotg210->enabled_hrtimer_events = 0; - fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; - - /* - * Check each pending event. If its time has expired, handle - * the event; otherwise re-enable it. - */ - now = ktime_get(); - for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) { - if (now.tv64 >= fotg210->hr_timeouts[e].tv64) - event_handlers[e](fotg210); - else - fotg210_enable_event(fotg210, e, false); - } - - spin_unlock_irqrestore(&fotg210->lock, flags); - return HRTIMER_NORESTART; -} - -/*-------------------------------------------------------------------------*/ - -#define fotg210_bus_suspend NULL -#define fotg210_bus_resume NULL - -/*-------------------------------------------------------------------------*/ - -static int check_reset_complete( - struct fotg210_hcd *fotg210, - int index, - u32 __iomem *status_reg, - int port_status -) { - if (!(port_status & PORT_CONNECT)) - return port_status; - - /* if reset finished and it's still not enabled -- handoff */ - if (!(port_status & PORT_PE)) { - /* with integrated TT, there's nobody to hand it to! */ - fotg210_dbg(fotg210, - "Failed to enable port %d on root hub TT\n", - index+1); - return port_status; - } else { - fotg210_dbg(fotg210, "port %d reset complete, port enabled\n", - index + 1); - } - - return port_status; -} - -/*-------------------------------------------------------------------------*/ - - -/* build "status change" packet (one or two bytes) from HC registers */ - -static int -fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - u32 temp, status; - u32 mask; - int retval = 1; - unsigned long flags; - - /* init status to no-changes */ - buf[0] = 0; - - /* Inform the core about resumes-in-progress by returning - * a non-zero value even if there are no status changes. - */ - status = fotg210->resuming_ports; - - mask = PORT_CSC | PORT_PEC; - /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */ - - /* no hub change reports (bit 0) for now (power, ...) */ - - /* port N changes (bit N)? */ - spin_lock_irqsave(&fotg210->lock, flags); - - temp = fotg210_readl(fotg210, &fotg210->regs->port_status); - - /* - * Return status information even for ports with OWNER set. - * Otherwise khubd wouldn't see the disconnect event when a - * high-speed device is switched over to the companion - * controller by the user. - */ - - if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) - || (fotg210->reset_done[0] && time_after_eq( - jiffies, fotg210->reset_done[0]))) { - buf[0] |= 1 << 1; - status = STS_PCD; - } - /* FIXME autosuspend idle root hubs */ - spin_unlock_irqrestore(&fotg210->lock, flags); - return status ? retval : 0; -} - -/*-------------------------------------------------------------------------*/ - -static void -fotg210_hub_descriptor( - struct fotg210_hcd *fotg210, - struct usb_hub_descriptor *desc -) { - int ports = HCS_N_PORTS(fotg210->hcs_params); - u16 temp; - - desc->bDescriptorType = 0x29; - desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */ - desc->bHubContrCurrent = 0; - - desc->bNbrPorts = ports; - temp = 1 + (ports / 8); - desc->bDescLength = 7 + 2 * temp; - - /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ - memset(&desc->u.hs.DeviceRemovable[0], 0, temp); - memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); - - temp = 0x0008; /* per-port overcurrent reporting */ - temp |= 0x0002; /* no power switching */ - desc->wHubCharacteristics = cpu_to_le16(temp); -} - -/*-------------------------------------------------------------------------*/ - -static int fotg210_hub_control( - struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, - u16 wIndex, - char *buf, - u16 wLength -) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - int ports = HCS_N_PORTS(fotg210->hcs_params); - u32 __iomem *status_reg = &fotg210->regs->port_status; - u32 temp, temp1, status; - unsigned long flags; - int retval = 0; - unsigned selector; - - /* - * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. - * HCS_INDICATOR may say we can change LEDs to off/amber/green. - * (track current state ourselves) ... blink for diagnostics, - * power, "this is the one", etc. EHCI spec supports this. - */ - - spin_lock_irqsave(&fotg210->lock, flags); - switch (typeReq) { - case ClearHubFeature: - switch (wValue) { - case C_HUB_LOCAL_POWER: - case C_HUB_OVER_CURRENT: - /* no hub-wide feature/status flags */ - break; - default: - goto error; - } - break; - case ClearPortFeature: - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - temp = fotg210_readl(fotg210, status_reg); - temp &= ~PORT_RWC_BITS; - - /* - * Even if OWNER is set, so the port is owned by the - * companion controller, khubd needs to be able to clear - * the port-change status bits (especially - * USB_PORT_STAT_C_CONNECTION). - */ - - switch (wValue) { - case USB_PORT_FEAT_ENABLE: - fotg210_writel(fotg210, temp & ~PORT_PE, status_reg); - break; - case USB_PORT_FEAT_C_ENABLE: - fotg210_writel(fotg210, temp | PORT_PEC, status_reg); - break; - case USB_PORT_FEAT_SUSPEND: - if (temp & PORT_RESET) - goto error; - if (!(temp & PORT_SUSPEND)) - break; - if ((temp & PORT_PE) == 0) - goto error; - - /* resume signaling for 20 msec */ - fotg210_writel(fotg210, temp | PORT_RESUME, status_reg); - fotg210->reset_done[wIndex] = jiffies - + msecs_to_jiffies(20); - break; - case USB_PORT_FEAT_C_SUSPEND: - clear_bit(wIndex, &fotg210->port_c_suspend); - break; - case USB_PORT_FEAT_C_CONNECTION: - fotg210_writel(fotg210, temp | PORT_CSC, status_reg); - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - fotg210_writel(fotg210, temp | OTGISR_OVC, - &fotg210->regs->otgisr); - break; - case USB_PORT_FEAT_C_RESET: - /* GetPortStatus clears reset */ - break; - default: - goto error; - } - fotg210_readl(fotg210, &fotg210->regs->command); - break; - case GetHubDescriptor: - fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *) - buf); - break; - case GetHubStatus: - /* no hub-wide feature/status flags */ - memset(buf, 0, 4); - /*cpu_to_le32s ((u32 *) buf); */ - break; - case GetPortStatus: - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - status = 0; - temp = fotg210_readl(fotg210, status_reg); - - /* wPortChange bits */ - if (temp & PORT_CSC) - status |= USB_PORT_STAT_C_CONNECTION << 16; - if (temp & PORT_PEC) - status |= USB_PORT_STAT_C_ENABLE << 16; - - temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); - if (temp1 & OTGISR_OVC) - status |= USB_PORT_STAT_C_OVERCURRENT << 16; - - /* whoever resumes must GetPortStatus to complete it!! */ - if (temp & PORT_RESUME) { - - /* Remote Wakeup received? */ - if (!fotg210->reset_done[wIndex]) { - /* resume signaling for 20 msec */ - fotg210->reset_done[wIndex] = jiffies - + msecs_to_jiffies(20); - /* check the port again */ - mod_timer(&fotg210_to_hcd(fotg210)->rh_timer, - fotg210->reset_done[wIndex]); - } - - /* resume completed? */ - else if (time_after_eq(jiffies, - fotg210->reset_done[wIndex])) { - clear_bit(wIndex, &fotg210->suspended_ports); - set_bit(wIndex, &fotg210->port_c_suspend); - fotg210->reset_done[wIndex] = 0; - - /* stop resume signaling */ - temp = fotg210_readl(fotg210, status_reg); - fotg210_writel(fotg210, - temp & ~(PORT_RWC_BITS | PORT_RESUME), - status_reg); - clear_bit(wIndex, &fotg210->resuming_ports); - retval = handshake(fotg210, status_reg, - PORT_RESUME, 0, 2000 /* 2msec */); - if (retval != 0) { - fotg210_err(fotg210, - "port %d resume error %d\n", - wIndex + 1, retval); - goto error; - } - temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); - } - } - - /* whoever resets must GetPortStatus to complete it!! */ - if ((temp & PORT_RESET) - && time_after_eq(jiffies, - fotg210->reset_done[wIndex])) { - status |= USB_PORT_STAT_C_RESET << 16; - fotg210->reset_done[wIndex] = 0; - clear_bit(wIndex, &fotg210->resuming_ports); - - /* force reset to complete */ - fotg210_writel(fotg210, - temp & ~(PORT_RWC_BITS | PORT_RESET), - status_reg); - /* REVISIT: some hardware needs 550+ usec to clear - * this bit; seems too long to spin routinely... - */ - retval = handshake(fotg210, status_reg, - PORT_RESET, 0, 1000); - if (retval != 0) { - fotg210_err(fotg210, "port %d reset error %d\n", - wIndex + 1, retval); - goto error; - } - - /* see what we found out */ - temp = check_reset_complete(fotg210, wIndex, status_reg, - fotg210_readl(fotg210, status_reg)); - } - - if (!(temp & (PORT_RESUME|PORT_RESET))) { - fotg210->reset_done[wIndex] = 0; - clear_bit(wIndex, &fotg210->resuming_ports); - } - - /* transfer dedicated ports to the companion hc */ - if ((temp & PORT_CONNECT) && - test_bit(wIndex, &fotg210->companion_ports)) { - temp &= ~PORT_RWC_BITS; - fotg210_writel(fotg210, temp, status_reg); - fotg210_dbg(fotg210, "port %d --> companion\n", - wIndex + 1); - temp = fotg210_readl(fotg210, status_reg); - } - - /* - * Even if OWNER is set, there's no harm letting khubd - * see the wPortStatus values (they should all be 0 except - * for PORT_POWER anyway). - */ - - if (temp & PORT_CONNECT) { - status |= USB_PORT_STAT_CONNECTION; - status |= fotg210_port_speed(fotg210, temp); - } - if (temp & PORT_PE) - status |= USB_PORT_STAT_ENABLE; - - /* maybe the port was unsuspended without our knowledge */ - if (temp & (PORT_SUSPEND|PORT_RESUME)) { - status |= USB_PORT_STAT_SUSPEND; - } else if (test_bit(wIndex, &fotg210->suspended_ports)) { - clear_bit(wIndex, &fotg210->suspended_ports); - clear_bit(wIndex, &fotg210->resuming_ports); - fotg210->reset_done[wIndex] = 0; - if (temp & PORT_PE) - set_bit(wIndex, &fotg210->port_c_suspend); - } - - temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); - if (temp1 & OTGISR_OVC) - status |= USB_PORT_STAT_OVERCURRENT; - if (temp & PORT_RESET) - status |= USB_PORT_STAT_RESET; - if (test_bit(wIndex, &fotg210->port_c_suspend)) - status |= USB_PORT_STAT_C_SUSPEND << 16; - -#ifndef VERBOSE_DEBUG - if (status & ~0xffff) /* only if wPortChange is interesting */ -#endif - dbg_port(fotg210, "GetStatus", wIndex + 1, temp); - put_unaligned_le32(status, buf); - break; - case SetHubFeature: - switch (wValue) { - case C_HUB_LOCAL_POWER: - case C_HUB_OVER_CURRENT: - /* no hub-wide feature/status flags */ - break; - default: - goto error; - } - break; - case SetPortFeature: - selector = wIndex >> 8; - wIndex &= 0xff; - - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - temp = fotg210_readl(fotg210, status_reg); - temp &= ~PORT_RWC_BITS; - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - if ((temp & PORT_PE) == 0 - || (temp & PORT_RESET) != 0) - goto error; - - /* After above check the port must be connected. - * Set appropriate bit thus could put phy into low power - * mode if we have hostpc feature - */ - fotg210_writel(fotg210, temp | PORT_SUSPEND, - status_reg); - set_bit(wIndex, &fotg210->suspended_ports); - break; - case USB_PORT_FEAT_RESET: - if (temp & PORT_RESUME) - goto error; - /* line status bits may report this as low speed, - * which can be fine if this root hub has a - * transaction translator built in. - */ - fotg210_vdbg(fotg210, "port %d reset\n", wIndex + 1); - temp |= PORT_RESET; - temp &= ~PORT_PE; - - /* - * caller must wait, then call GetPortStatus - * usb 2.0 spec says 50 ms resets on root - */ - fotg210->reset_done[wIndex] = jiffies - + msecs_to_jiffies(50); - fotg210_writel(fotg210, temp, status_reg); - break; - - /* For downstream facing ports (these): one hub port is put - * into test mode according to USB2 11.24.2.13, then the hub - * must be reset (which for root hub now means rmmod+modprobe, - * or else system reboot). See EHCI 2.3.9 and 4.14 for info - * about the EHCI-specific stuff. - */ - case USB_PORT_FEAT_TEST: - if (!selector || selector > 5) - goto error; - spin_unlock_irqrestore(&fotg210->lock, flags); - fotg210_quiesce(fotg210); - spin_lock_irqsave(&fotg210->lock, flags); - - /* Put all enabled ports into suspend */ - temp = fotg210_readl(fotg210, status_reg) & - ~PORT_RWC_BITS; - if (temp & PORT_PE) - fotg210_writel(fotg210, temp | PORT_SUSPEND, - status_reg); - - spin_unlock_irqrestore(&fotg210->lock, flags); - fotg210_halt(fotg210); - spin_lock_irqsave(&fotg210->lock, flags); - - temp = fotg210_readl(fotg210, status_reg); - temp |= selector << 16; - fotg210_writel(fotg210, temp, status_reg); - break; - - default: - goto error; - } - fotg210_readl(fotg210, &fotg210->regs->command); - break; - - default: -error: - /* "stall" on error */ - retval = -EPIPE; - } - spin_unlock_irqrestore(&fotg210->lock, flags); - return retval; -} - -static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd, - int portnum) -{ - return; -} - -static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, - int portnum) -{ - return 0; -} -/*-------------------------------------------------------------------------*/ -/* - * There's basically three types of memory: - * - data used only by the HCD ... kmalloc is fine - * - async and periodic schedules, shared by HC and HCD ... these - * need to use dma_pool or dma_alloc_coherent - * - driver buffers, read/written by HC ... single shot DMA mapped - * - * There's also "register" data (e.g. PCI or SOC), which is memory mapped. - * No memory seen by this driver is pageable. - */ - -/*-------------------------------------------------------------------------*/ - -/* Allocate the key transfer structures from the previously allocated pool */ - -static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, - struct fotg210_qtd *qtd, dma_addr_t dma) -{ - memset(qtd, 0, sizeof(*qtd)); - qtd->qtd_dma = dma; - qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); - qtd->hw_next = FOTG210_LIST_END(fotg210); - qtd->hw_alt_next = FOTG210_LIST_END(fotg210); - INIT_LIST_HEAD(&qtd->qtd_list); -} - -static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, - gfp_t flags) -{ - struct fotg210_qtd *qtd; - dma_addr_t dma; - - qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma); - if (qtd != NULL) - fotg210_qtd_init(fotg210, qtd, dma); - - return qtd; -} - -static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210, - struct fotg210_qtd *qtd) -{ - dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma); -} - - -static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{ - /* clean qtds first, and know this is not linked */ - if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { - fotg210_dbg(fotg210, "unused qh not empty!\n"); - BUG(); - } - if (qh->dummy) - fotg210_qtd_free(fotg210, qh->dummy); - dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); - kfree(qh); -} - -static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, - gfp_t flags) -{ - struct fotg210_qh *qh; - dma_addr_t dma; - - qh = kzalloc(sizeof(*qh), GFP_ATOMIC); - if (!qh) - goto done; - qh->hw = (struct fotg210_qh_hw *) - dma_pool_alloc(fotg210->qh_pool, flags, &dma); - if (!qh->hw) - goto fail; - memset(qh->hw, 0, sizeof(*qh->hw)); - qh->qh_dma = dma; - INIT_LIST_HEAD(&qh->qtd_list); - - /* dummy td enables safe urb queuing */ - qh->dummy = fotg210_qtd_alloc(fotg210, flags); - if (qh->dummy == NULL) { - fotg210_dbg(fotg210, "no dummy td\n"); - goto fail1; - } -done: - return qh; -fail1: - dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); -fail: - kfree(qh); - return NULL; -} - -/*-------------------------------------------------------------------------*/ - -/* The queue heads and transfer descriptors are managed from pools tied - * to each of the "per device" structures. - * This is the initialisation and cleanup code. - */ - -static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) -{ - if (fotg210->async) - qh_destroy(fotg210, fotg210->async); - fotg210->async = NULL; - - if (fotg210->dummy) - qh_destroy(fotg210, fotg210->dummy); - fotg210->dummy = NULL; - - /* DMA consistent memory and pools */ - if (fotg210->qtd_pool) - dma_pool_destroy(fotg210->qtd_pool); - fotg210->qtd_pool = NULL; - - if (fotg210->qh_pool) { - dma_pool_destroy(fotg210->qh_pool); - fotg210->qh_pool = NULL; - } - - if (fotg210->itd_pool) - dma_pool_destroy(fotg210->itd_pool); - fotg210->itd_pool = NULL; - - if (fotg210->periodic) - dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, - fotg210->periodic_size * sizeof(u32), - fotg210->periodic, fotg210->periodic_dma); - fotg210->periodic = NULL; - - /* shadow periodic table */ - kfree(fotg210->pshadow); - fotg210->pshadow = NULL; -} - -/* remember to add cleanup code (above) if you add anything here */ -static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) -{ - int i; - - /* QTDs for control/bulk/intr transfers */ - fotg210->qtd_pool = dma_pool_create("fotg210_qtd", - fotg210_to_hcd(fotg210)->self.controller, - sizeof(struct fotg210_qtd), - 32 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */); - if (!fotg210->qtd_pool) - goto fail; - - /* QHs for control/bulk/intr transfers */ - fotg210->qh_pool = dma_pool_create("fotg210_qh", - fotg210_to_hcd(fotg210)->self.controller, - sizeof(struct fotg210_qh_hw), - 32 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */); - if (!fotg210->qh_pool) - goto fail; - - fotg210->async = fotg210_qh_alloc(fotg210, flags); - if (!fotg210->async) - goto fail; - - /* ITD for high speed ISO transfers */ - fotg210->itd_pool = dma_pool_create("fotg210_itd", - fotg210_to_hcd(fotg210)->self.controller, - sizeof(struct fotg210_itd), - 64 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */); - if (!fotg210->itd_pool) - goto fail; - - /* Hardware periodic table */ - fotg210->periodic = (__le32 *) - dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, - fotg210->periodic_size * sizeof(__le32), - &fotg210->periodic_dma, 0); - if (fotg210->periodic == NULL) - goto fail; - - for (i = 0; i < fotg210->periodic_size; i++) - fotg210->periodic[i] = FOTG210_LIST_END(fotg210); - - /* software shadow of hardware table */ - fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *), - flags); - if (fotg210->pshadow != NULL) - return 0; - -fail: - fotg210_dbg(fotg210, "couldn't init memory\n"); - fotg210_mem_cleanup(fotg210); - return -ENOMEM; -} -/*-------------------------------------------------------------------------*/ -/* - * EHCI hardware queue manipulation ... the core. QH/QTD manipulation. - * - * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" - * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned - * buffers needed for the larger number). We use one QH per endpoint, queue - * multiple urbs (all three types) per endpoint. URBs may need several qtds. - * - * ISO traffic uses "ISO TD" (itd) records, and (along with - * interrupts) needs careful scheduling. Performance improvements can be - * an ongoing challenge. That's in "ehci-sched.c". - * - * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, - * or otherwise through transaction translators (TTs) in USB 2.0 hubs using - * (b) special fields in qh entries or (c) split iso entries. TTs will - * buffer low/full speed data so the host collects it at high speed. - */ - -/*-------------------------------------------------------------------------*/ - -/* fill a qtd, returning how much of the buffer we were able to queue up */ - -static int -qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, dma_addr_t buf, - size_t len, int token, int maxpacket) -{ - int i, count; - u64 addr = buf; - - /* one buffer entry per 4K ... first might be short or unaligned */ - qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr); - qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32)); - count = 0x1000 - (buf & 0x0fff); /* rest of that page */ - if (likely(len < count)) /* ... iff needed */ - count = len; - else { - buf += 0x1000; - buf &= ~0x0fff; - - /* per-qtd limit: from 16K to 20K (best alignment) */ - for (i = 1; count < len && i < 5; i++) { - addr = buf; - qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr); - qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210, - (u32)(addr >> 32)); - buf += 0x1000; - if ((count + 0x1000) < len) - count += 0x1000; - else - count = len; - } - - /* short packets may only terminate transfers */ - if (count != len) - count -= (count % maxpacket); - } - qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token); - qtd->length = count; - - return count; -} - -/*-------------------------------------------------------------------------*/ - -static inline void -qh_update(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, - struct fotg210_qtd *qtd) -{ - struct fotg210_qh_hw *hw = qh->hw; - - /* writes to an active overlay are unsafe */ - BUG_ON(qh->qh_state != QH_STATE_IDLE); - - hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma); - hw->hw_alt_next = FOTG210_LIST_END(fotg210); - - /* Except for control endpoints, we make hardware maintain data - * toggle (like OHCI) ... here (re)initialize the toggle in the QH, - * and set the pseudo-toggle in udev. Only usb_clear_halt() will - * ever clear it. - */ - if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) { - unsigned is_out, epnum; - - is_out = qh->is_out; - epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f; - if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { - hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE); - usb_settoggle(qh->dev, epnum, is_out, 1); - } - } - - hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING); -} - -/* if it weren't for a common silicon quirk (writing the dummy into the qh - * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault - * recovery (including urb dequeue) would need software changes to a QH... - */ -static void -qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{ - struct fotg210_qtd *qtd; - - if (list_empty(&qh->qtd_list)) - qtd = qh->dummy; - else { - qtd = list_entry(qh->qtd_list.next, - struct fotg210_qtd, qtd_list); - /* - * first qtd may already be partially processed. - * If we come here during unlink, the QH overlay region - * might have reference to the just unlinked qtd. The - * qtd is updated in qh_completions(). Update the QH - * overlay here. - */ - if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) { - qh->hw->hw_qtd_next = qtd->hw_next; - qtd = NULL; - } - } - - if (qtd) - qh_update(fotg210, qh, qtd); -} - -/*-------------------------------------------------------------------------*/ - -static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); - -static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - struct fotg210_qh *qh = ep->hcpriv; - unsigned long flags; - - spin_lock_irqsave(&fotg210->lock, flags); - qh->clearing_tt = 0; - if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) - && fotg210->rh_state == FOTG210_RH_RUNNING) - qh_link_async(fotg210, qh); - spin_unlock_irqrestore(&fotg210->lock, flags); -} - -static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh, - struct urb *urb, u32 token) -{ - - /* If an async split transaction gets an error or is unlinked, - * the TT buffer may be left in an indeterminate state. We - * have to clear the TT buffer. - * - * Note: this routine is never called for Isochronous transfers. - */ - if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { -#ifdef DEBUG - struct usb_device *tt = urb->dev->tt->hub; - dev_dbg(&tt->dev, - "clear tt buffer port %d, a%d ep%d t%08x\n", - urb->dev->ttport, urb->dev->devnum, - usb_pipeendpoint(urb->pipe), token); -#endif /* DEBUG */ - if (urb->dev->tt->hub != - fotg210_to_hcd(fotg210)->self.root_hub) { - if (usb_hub_clear_tt_buffer(urb) == 0) - qh->clearing_tt = 1; - } - } -} - -static int qtd_copy_status( - struct fotg210_hcd *fotg210, - struct urb *urb, - size_t length, - u32 token -) -{ - int status = -EINPROGRESS; - - /* count IN/OUT bytes, not SETUP (even short packets) */ - if (likely(QTD_PID(token) != 2)) - urb->actual_length += length - QTD_LENGTH(token); - - /* don't modify error codes */ - if (unlikely(urb->unlinked)) - return status; - - /* force cleanup after short read; not always an error */ - if (unlikely(IS_SHORT_READ(token))) - status = -EREMOTEIO; - - /* serious "can't proceed" faults reported by the hardware */ - if (token & QTD_STS_HALT) { - if (token & QTD_STS_BABBLE) { - /* FIXME "must" disable babbling device's port too */ - status = -EOVERFLOW; - /* CERR nonzero + halt --> stall */ - } else if (QTD_CERR(token)) { - status = -EPIPE; - - /* In theory, more than one of the following bits can be set - * since they are sticky and the transaction is retried. - * Which to test first is rather arbitrary. - */ - } else if (token & QTD_STS_MMF) { - /* fs/ls interrupt xfer missed the complete-split */ - status = -EPROTO; - } else if (token & QTD_STS_DBE) { - status = (QTD_PID(token) == 1) /* IN ? */ - ? -ENOSR /* hc couldn't read data */ - : -ECOMM; /* hc couldn't write data */ - } else if (token & QTD_STS_XACT) { - /* timeout, bad CRC, wrong PID, etc */ - fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n", - urb->dev->devpath, - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out"); - status = -EPROTO; - } else { /* unknown */ - status = -EPROTO; - } - - fotg210_vdbg(fotg210, - "dev%d ep%d%s qtd token %08x --> status %d\n", - usb_pipedevice(urb->pipe), - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out", - token, status); - } - - return status; -} - -static void -fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, int status) -__releases(fotg210->lock) -__acquires(fotg210->lock) -{ - if (likely(urb->hcpriv != NULL)) { - struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; - - /* S-mask in a QH means it's an interrupt urb */ - if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) { - - /* ... update hc-wide periodic stats (for usbfs) */ - fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--; - } - } - - if (unlikely(urb->unlinked)) { - COUNT(fotg210->stats.unlink); - } else { - /* report non-error and short read status as zero */ - if (status == -EINPROGRESS || status == -EREMOTEIO) - status = 0; - COUNT(fotg210->stats.complete); - } - -#ifdef FOTG210_URB_TRACE - fotg210_dbg(fotg210, - "%s %s urb %p ep%d%s status %d len %d/%d\n", - __func__, urb->dev->devpath, urb, - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out", - status, - urb->actual_length, urb->transfer_buffer_length); -#endif - - /* complete() can reenter this HCD */ - usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); - spin_unlock(&fotg210->lock); - usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status); - spin_lock(&fotg210->lock); -} - -static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); - -/* - * Process and free completed qtds for a qh, returning URBs to drivers. - * Chases up to qh->hw_current. Returns number of completions called, - * indicating how much "real" work we did. - */ -static unsigned -qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{ - struct fotg210_qtd *last, *end = qh->dummy; - struct list_head *entry, *tmp; - int last_status; - int stopped; - unsigned count = 0; - u8 state; - struct fotg210_qh_hw *hw = qh->hw; - - if (unlikely(list_empty(&qh->qtd_list))) - return count; - - /* completions (or tasks on other cpus) must never clobber HALT - * till we've gone through and cleaned everything up, even when - * they add urbs to this qh's queue or mark them for unlinking. - * - * NOTE: unlinking expects to be done in queue order. - * - * It's a bug for qh->qh_state to be anything other than - * QH_STATE_IDLE, unless our caller is scan_async() or - * scan_intr(). - */ - state = qh->qh_state; - qh->qh_state = QH_STATE_COMPLETING; - stopped = (state == QH_STATE_IDLE); - - rescan: - last = NULL; - last_status = -EINPROGRESS; - qh->needs_rescan = 0; - - /* remove de-activated QTDs from front of queue. - * after faults (including short reads), cleanup this urb - * then let the queue advance. - * if queue is stopped, handles unlinks. - */ - list_for_each_safe(entry, tmp, &qh->qtd_list) { - struct fotg210_qtd *qtd; - struct urb *urb; - u32 token = 0; - - qtd = list_entry(entry, struct fotg210_qtd, qtd_list); - urb = qtd->urb; - - /* clean up any state from previous QTD ...*/ - if (last) { - if (likely(last->urb != urb)) { - fotg210_urb_done(fotg210, last->urb, - last_status); - count++; - last_status = -EINPROGRESS; - } - fotg210_qtd_free(fotg210, last); - last = NULL; - } - - /* ignore urbs submitted during completions we reported */ - if (qtd == end) - break; - - /* hardware copies qtd out of qh overlay */ - rmb(); - token = hc32_to_cpu(fotg210, qtd->hw_token); - - /* always clean up qtds the hc de-activated */ - retry_xacterr: - if ((token & QTD_STS_ACTIVE) == 0) { - - /* Report Data Buffer Error: non-fatal but useful */ - if (token & QTD_STS_DBE) - fotg210_dbg(fotg210, - "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", - urb, - usb_endpoint_num(&urb->ep->desc), - usb_endpoint_dir_in(&urb->ep->desc) - ? "in" : "out", - urb->transfer_buffer_length, - qtd, - qh); - - /* on STALL, error, and short reads this urb must - * complete and all its qtds must be recycled. - */ - if ((token & QTD_STS_HALT) != 0) { - - /* retry transaction errors until we - * reach the software xacterr limit - */ - if ((token & QTD_STS_XACT) && - QTD_CERR(token) == 0 && - ++qh->xacterrs < QH_XACTERR_MAX && - !urb->unlinked) { - fotg210_dbg(fotg210, - "detected XactErr len %zu/%zu retry %d\n", - qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs); - - /* reset the token in the qtd and the - * qh overlay (which still contains - * the qtd) so that we pick up from - * where we left off - */ - token &= ~QTD_STS_HALT; - token |= QTD_STS_ACTIVE | - (FOTG210_TUNE_CERR << 10); - qtd->hw_token = cpu_to_hc32(fotg210, - token); - wmb(); - hw->hw_token = cpu_to_hc32(fotg210, - token); - goto retry_xacterr; - } - stopped = 1; - - /* magic dummy for some short reads; qh won't advance. - * that silicon quirk can kick in with this dummy too. - * - * other short reads won't stop the queue, including - * control transfers (status stage handles that) or - * most other single-qtd reads ... the queue stops if - * URB_SHORT_NOT_OK was set so the driver submitting - * the urbs could clean it up. - */ - } else if (IS_SHORT_READ(token) - && !(qtd->hw_alt_next - & FOTG210_LIST_END(fotg210))) { - stopped = 1; - } - - /* stop scanning when we reach qtds the hc is using */ - } else if (likely(!stopped - && fotg210->rh_state >= FOTG210_RH_RUNNING)) { - break; - - /* scan the whole queue for unlinks whenever it stops */ - } else { - stopped = 1; - - /* cancel everything if we halt, suspend, etc */ - if (fotg210->rh_state < FOTG210_RH_RUNNING) - last_status = -ESHUTDOWN; - - /* this qtd is active; skip it unless a previous qtd - * for its urb faulted, or its urb was canceled. - */ - else if (last_status == -EINPROGRESS && !urb->unlinked) - continue; - - /* qh unlinked; token in overlay may be most current */ - if (state == QH_STATE_IDLE - && cpu_to_hc32(fotg210, qtd->qtd_dma) - == hw->hw_current) { - token = hc32_to_cpu(fotg210, hw->hw_token); - - /* An unlink may leave an incomplete - * async transaction in the TT buffer. - * We have to clear it. - */ - fotg210_clear_tt_buffer(fotg210, qh, urb, - token); - } - } - - /* unless we already know the urb's status, collect qtd status - * and update count of bytes transferred. in common short read - * cases with only one data qtd (including control transfers), - * queue processing won't halt. but with two or more qtds (for - * example, with a 32 KB transfer), when the first qtd gets a - * short read the second must be removed by hand. - */ - if (last_status == -EINPROGRESS) { - last_status = qtd_copy_status(fotg210, urb, - qtd->length, token); - if (last_status == -EREMOTEIO - && (qtd->hw_alt_next - & FOTG210_LIST_END(fotg210))) - last_status = -EINPROGRESS; - - /* As part of low/full-speed endpoint-halt processing - * we must clear the TT buffer (11.17.5). - */ - if (unlikely(last_status != -EINPROGRESS && - last_status != -EREMOTEIO)) { - /* The TT's in some hubs malfunction when they - * receive this request following a STALL (they - * stop sending isochronous packets). Since a - * STALL can't leave the TT buffer in a busy - * state (if you believe Figures 11-48 - 11-51 - * in the USB 2.0 spec), we won't clear the TT - * buffer in this case. Strictly speaking this - * is a violation of the spec. - */ - if (last_status != -EPIPE) - fotg210_clear_tt_buffer(fotg210, qh, - urb, token); - } - } - - /* if we're removing something not at the queue head, - * patch the hardware queue pointer. - */ - if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { - last = list_entry(qtd->qtd_list.prev, - struct fotg210_qtd, qtd_list); - last->hw_next = qtd->hw_next; - } - - /* remove qtd; it's recycled after possible urb completion */ - list_del(&qtd->qtd_list); - last = qtd; - - /* reinit the xacterr counter for the next qtd */ - qh->xacterrs = 0; - } - - /* last urb's completion might still need calling */ - if (likely(last != NULL)) { - fotg210_urb_done(fotg210, last->urb, last_status); - count++; - fotg210_qtd_free(fotg210, last); - } - - /* Do we need to rescan for URBs dequeued during a giveback? */ - if (unlikely(qh->needs_rescan)) { - /* If the QH is already unlinked, do the rescan now. */ - if (state == QH_STATE_IDLE) - goto rescan; - - /* Otherwise we have to wait until the QH is fully unlinked. - * Our caller will start an unlink if qh->needs_rescan is - * set. But if an unlink has already started, nothing needs - * to be done. - */ - if (state != QH_STATE_LINKED) - qh->needs_rescan = 0; - } - - /* restore original state; caller must unlink or relink */ - qh->qh_state = state; - - /* be sure the hardware's done with the qh before refreshing - * it after fault cleanup, or recovering from silicon wrongly - * overlaying the dummy qtd (which reduces DMA chatter). - */ - if (stopped != 0 || hw->hw_qtd_next == FOTG210_LIST_END(fotg210)) { - switch (state) { - case QH_STATE_IDLE: - qh_refresh(fotg210, qh); - break; - case QH_STATE_LINKED: - /* We won't refresh a QH that's linked (after the HC - * stopped the queue). That avoids a race: - * - HC reads first part of QH; - * - CPU updates that first part and the token; - * - HC reads rest of that QH, including token - * Result: HC gets an inconsistent image, and then - * DMAs to/from the wrong memory (corrupting it). - * - * That should be rare for interrupt transfers, - * except maybe high bandwidth ... - */ - - /* Tell the caller to start an unlink */ - qh->needs_rescan = 1; - break; - /* otherwise, unlink already started */ - } - } - - return count; -} - -/*-------------------------------------------------------------------------*/ - -/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ -#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) -/* ... and packet size, for any kind of endpoint descriptor */ -#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) - -/* - * reverse of qh_urb_transaction: free a list of TDs. - * used for cleanup after errors, before HC sees an URB's TDs. - */ -static void qtd_list_free( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *qtd_list -) { - struct list_head *entry, *temp; - - list_for_each_safe(entry, temp, qtd_list) { - struct fotg210_qtd *qtd; - - qtd = list_entry(entry, struct fotg210_qtd, qtd_list); - list_del(&qtd->qtd_list); - fotg210_qtd_free(fotg210, qtd); - } -} - -/* - * create a list of filled qtds for this URB; won't link into qh. - */ -static struct list_head * -qh_urb_transaction( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *head, - gfp_t flags -) { - struct fotg210_qtd *qtd, *qtd_prev; - dma_addr_t buf; - int len, this_sg_len, maxpacket; - int is_input; - u32 token; - int i; - struct scatterlist *sg; - - /* - * URBs map to sequences of QTDs: one logical transaction - */ - qtd = fotg210_qtd_alloc(fotg210, flags); - if (unlikely(!qtd)) - return NULL; - list_add_tail(&qtd->qtd_list, head); - qtd->urb = urb; - - token = QTD_STS_ACTIVE; - token |= (FOTG210_TUNE_CERR << 10); - /* for split transactions, SplitXState initialized to zero */ - - len = urb->transfer_buffer_length; - is_input = usb_pipein(urb->pipe); - if (usb_pipecontrol(urb->pipe)) { - /* SETUP pid */ - qtd_fill(fotg210, qtd, urb->setup_dma, - sizeof(struct usb_ctrlrequest), - token | (2 /* "setup" */ << 8), 8); - - /* ... and always at least one more pid */ - token ^= QTD_TOGGLE; - qtd_prev = qtd; - qtd = fotg210_qtd_alloc(fotg210, flags); - if (unlikely(!qtd)) - goto cleanup; - qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); - list_add_tail(&qtd->qtd_list, head); - - /* for zero length DATA stages, STATUS is always IN */ - if (len == 0) - token |= (1 /* "in" */ << 8); - } - - /* - * data transfer stage: buffer setup - */ - i = urb->num_mapped_sgs; - if (len > 0 && i > 0) { - sg = urb->sg; - buf = sg_dma_address(sg); - - /* urb->transfer_buffer_length may be smaller than the - * size of the scatterlist (or vice versa) - */ - this_sg_len = min_t(int, sg_dma_len(sg), len); - } else { - sg = NULL; - buf = urb->transfer_dma; - this_sg_len = len; - } - - if (is_input) - token |= (1 /* "in" */ << 8); - /* else it's already initted to "out" pid (0 << 8) */ - - maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); - - /* - * buffer gets wrapped in one or more qtds; - * last one may be "short" (including zero len) - * and may serve as a control status ack - */ - for (;;) { - int this_qtd_len; - - this_qtd_len = qtd_fill(fotg210, qtd, buf, this_sg_len, token, - maxpacket); - this_sg_len -= this_qtd_len; - len -= this_qtd_len; - buf += this_qtd_len; - - /* - * short reads advance to a "magic" dummy instead of the next - * qtd ... that forces the queue to stop, for manual cleanup. - * (this will usually be overridden later.) - */ - if (is_input) - qtd->hw_alt_next = fotg210->async->hw->hw_alt_next; - - /* qh makes control packets use qtd toggle; maybe switch it */ - if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) - token ^= QTD_TOGGLE; - - if (likely(this_sg_len <= 0)) { - if (--i <= 0 || len <= 0) - break; - sg = sg_next(sg); - buf = sg_dma_address(sg); - this_sg_len = min_t(int, sg_dma_len(sg), len); - } - - qtd_prev = qtd; - qtd = fotg210_qtd_alloc(fotg210, flags); - if (unlikely(!qtd)) - goto cleanup; - qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); - list_add_tail(&qtd->qtd_list, head); - } - - /* - * unless the caller requires manual cleanup after short reads, - * have the alt_next mechanism keep the queue running after the - * last data qtd (the only one, for control and most other cases). - */ - if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 - || usb_pipecontrol(urb->pipe))) - qtd->hw_alt_next = FOTG210_LIST_END(fotg210); - - /* - * control requests may need a terminating data "status" ack; - * other OUT ones may need a terminating short packet - * (zero length). - */ - if (likely(urb->transfer_buffer_length != 0)) { - int one_more = 0; - - if (usb_pipecontrol(urb->pipe)) { - one_more = 1; - token ^= 0x0100; /* "in" <--> "out" */ - token |= QTD_TOGGLE; /* force DATA1 */ - } else if (usb_pipeout(urb->pipe) - && (urb->transfer_flags & URB_ZERO_PACKET) - && !(urb->transfer_buffer_length % maxpacket)) { - one_more = 1; - } - if (one_more) { - qtd_prev = qtd; - qtd = fotg210_qtd_alloc(fotg210, flags); - if (unlikely(!qtd)) - goto cleanup; - qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); - list_add_tail(&qtd->qtd_list, head); - - /* never any data in such packets */ - qtd_fill(fotg210, qtd, 0, 0, token, 0); - } - } - - /* by default, enable interrupt on urb completion */ - if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) - qtd->hw_token |= cpu_to_hc32(fotg210, QTD_IOC); - return head; - -cleanup: - qtd_list_free(fotg210, urb, head); - return NULL; -} - -/*-------------------------------------------------------------------------*/ -/* - * Would be best to create all qh's from config descriptors, - * when each interface/altsetting is established. Unlink - * any previous qh and cancel its urbs first; endpoints are - * implicitly reset then (data toggle too). - * That'd mean updating how usbcore talks to HCDs. (2.7?) -*/ - - -/* - * Each QH holds a qtd list; a QH is used for everything except iso. - * - * For interrupt urbs, the scheduler must set the microframe scheduling - * mask(s) each time the QH gets scheduled. For highspeed, that's - * just one microframe in the s-mask. For split interrupt transactions - * there are additional complications: c-mask, maybe FSTNs. - */ -static struct fotg210_qh * -qh_make( - struct fotg210_hcd *fotg210, - struct urb *urb, - gfp_t flags -) { - struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); - u32 info1 = 0, info2 = 0; - int is_input, type; - int maxp = 0; - struct usb_tt *tt = urb->dev->tt; - struct fotg210_qh_hw *hw; - - if (!qh) - return qh; - - /* - * init endpoint/device data for this QH - */ - info1 |= usb_pipeendpoint(urb->pipe) << 8; - info1 |= usb_pipedevice(urb->pipe) << 0; - - is_input = usb_pipein(urb->pipe); - type = usb_pipetype(urb->pipe); - maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input); - - /* 1024 byte maxpacket is a hardware ceiling. High bandwidth - * acts like up to 3KB, but is built from smaller packets. - */ - if (max_packet(maxp) > 1024) { - fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", - max_packet(maxp)); - goto done; - } - - /* Compute interrupt scheduling parameters just once, and save. - * - allowing for high bandwidth, how many nsec/uframe are used? - * - split transactions need a second CSPLIT uframe; same question - * - splits also need a schedule gap (for full/low speed I/O) - * - qh has a polling interval - * - * For control/bulk requests, the HC or TT handles these. - */ - if (type == PIPE_INTERRUPT) { - qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, - is_input, 0, - hb_mult(maxp) * max_packet(maxp))); - qh->start = NO_FRAME; - - if (urb->dev->speed == USB_SPEED_HIGH) { - qh->c_usecs = 0; - qh->gap_uf = 0; - - qh->period = urb->interval >> 3; - if (qh->period == 0 && urb->interval != 1) { - /* NOTE interval 2 or 4 uframes could work. - * But interval 1 scheduling is simpler, and - * includes high bandwidth. - */ - urb->interval = 1; - } else if (qh->period > fotg210->periodic_size) { - qh->period = fotg210->periodic_size; - urb->interval = qh->period << 3; - } - } else { - int think_time; - - /* gap is f(FS/LS transfer times) */ - qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, - is_input, 0, maxp) / (125 * 1000); - - /* FIXME this just approximates SPLIT/CSPLIT times */ - if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ - qh->c_usecs = qh->usecs + HS_USECS(0); - qh->usecs = HS_USECS(1); - } else { /* SPLIT+DATA, gap, CSPLIT */ - qh->usecs += HS_USECS(1); - qh->c_usecs = HS_USECS(0); - } - - think_time = tt ? tt->think_time : 0; - qh->tt_usecs = NS_TO_US(think_time + - usb_calc_bus_time(urb->dev->speed, - is_input, 0, max_packet(maxp))); - qh->period = urb->interval; - if (qh->period > fotg210->periodic_size) { - qh->period = fotg210->periodic_size; - urb->interval = qh->period; - } - } - } - - /* support for tt scheduling, and access to toggles */ - qh->dev = urb->dev; - - /* using TT? */ - switch (urb->dev->speed) { - case USB_SPEED_LOW: - info1 |= QH_LOW_SPEED; - /* FALL THROUGH */ - - case USB_SPEED_FULL: - /* EPS 0 means "full" */ - if (type != PIPE_INTERRUPT) - info1 |= (FOTG210_TUNE_RL_TT << 28); - if (type == PIPE_CONTROL) { - info1 |= QH_CONTROL_EP; /* for TT */ - info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ - } - info1 |= maxp << 16; - - info2 |= (FOTG210_TUNE_MULT_TT << 30); - - /* Some Freescale processors have an erratum in which the - * port number in the queue head was 0..N-1 instead of 1..N. - */ - if (fotg210_has_fsl_portno_bug(fotg210)) - info2 |= (urb->dev->ttport-1) << 23; - else - info2 |= urb->dev->ttport << 23; - - /* set the address of the TT; for TDI's integrated - * root hub tt, leave it zeroed. - */ - if (tt && tt->hub != fotg210_to_hcd(fotg210)->self.root_hub) - info2 |= tt->hub->devnum << 16; - - /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ - - break; - - case USB_SPEED_HIGH: /* no TT involved */ - info1 |= QH_HIGH_SPEED; - if (type == PIPE_CONTROL) { - info1 |= (FOTG210_TUNE_RL_HS << 28); - info1 |= 64 << 16; /* usb2 fixed maxpacket */ - info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ - info2 |= (FOTG210_TUNE_MULT_HS << 30); - } else if (type == PIPE_BULK) { - info1 |= (FOTG210_TUNE_RL_HS << 28); - /* The USB spec says that high speed bulk endpoints - * always use 512 byte maxpacket. But some device - * vendors decided to ignore that, and MSFT is happy - * to help them do so. So now people expect to use - * such nonconformant devices with Linux too; sigh. - */ - info1 |= max_packet(maxp) << 16; - info2 |= (FOTG210_TUNE_MULT_HS << 30); - } else { /* PIPE_INTERRUPT */ - info1 |= max_packet(maxp) << 16; - info2 |= hb_mult(maxp) << 30; - } - break; - default: - fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev, - urb->dev->speed); -done: - qh_destroy(fotg210, qh); - return NULL; - } - - /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ - - /* init as live, toggle clear, advance to dummy */ - qh->qh_state = QH_STATE_IDLE; - hw = qh->hw; - hw->hw_info1 = cpu_to_hc32(fotg210, info1); - hw->hw_info2 = cpu_to_hc32(fotg210, info2); - qh->is_out = !is_input; - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); - qh_refresh(fotg210, qh); - return qh; -} - -/*-------------------------------------------------------------------------*/ - -static void enable_async(struct fotg210_hcd *fotg210) -{ - if (fotg210->async_count++) - return; - - /* Stop waiting to turn off the async schedule */ - fotg210->enabled_hrtimer_events &= ~BIT(FOTG210_HRTIMER_DISABLE_ASYNC); - - /* Don't start the schedule until ASS is 0 */ - fotg210_poll_ASS(fotg210); - turn_on_io_watchdog(fotg210); -} - -static void disable_async(struct fotg210_hcd *fotg210) -{ - if (--fotg210->async_count) - return; - - /* The async schedule and async_unlink list are supposed to be empty */ - WARN_ON(fotg210->async->qh_next.qh || fotg210->async_unlink); - - /* Don't turn off the schedule until ASS is 1 */ - fotg210_poll_ASS(fotg210); -} - -/* move qh (and its qtds) onto async queue; maybe enable queue. */ - -static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{ - __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); - struct fotg210_qh *head; - - /* Don't link a QH if there's a Clear-TT-Buffer pending */ - if (unlikely(qh->clearing_tt)) - return; - - WARN_ON(qh->qh_state != QH_STATE_IDLE); - - /* clear halt and/or toggle; and maybe recover from silicon quirk */ - qh_refresh(fotg210, qh); - - /* splice right after start */ - head = fotg210->async; - qh->qh_next = head->qh_next; - qh->hw->hw_next = head->hw->hw_next; - wmb(); - - head->qh_next.qh = qh; - head->hw->hw_next = dma; - - qh->xacterrs = 0; - qh->qh_state = QH_STATE_LINKED; - /* qtd completions reported later by interrupt */ - - enable_async(fotg210); -} - -/*-------------------------------------------------------------------------*/ - -/* - * For control/bulk/interrupt, return QH with these TDs appended. - * Allocates and initializes the QH if necessary. - * Returns null if it can't allocate a QH it needs to. - * If the QH has TDs (urbs) already, that's great. - */ -static struct fotg210_qh *qh_append_tds( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *qtd_list, - int epnum, - void **ptr -) -{ - struct fotg210_qh *qh = NULL; - __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); - - qh = (struct fotg210_qh *) *ptr; - if (unlikely(qh == NULL)) { - /* can't sleep here, we have fotg210->lock... */ - qh = qh_make(fotg210, urb, GFP_ATOMIC); - *ptr = qh; - } - if (likely(qh != NULL)) { - struct fotg210_qtd *qtd; - - if (unlikely(list_empty(qtd_list))) - qtd = NULL; - else - qtd = list_entry(qtd_list->next, struct fotg210_qtd, - qtd_list); - - /* control qh may need patching ... */ - if (unlikely(epnum == 0)) { - /* usb_reset_device() briefly reverts to address 0 */ - if (usb_pipedevice(urb->pipe) == 0) - qh->hw->hw_info1 &= ~qh_addr_mask; - } - - /* just one way to queue requests: swap with the dummy qtd. - * only hc or qh_refresh() ever modify the overlay. - */ - if (likely(qtd != NULL)) { - struct fotg210_qtd *dummy; - dma_addr_t dma; - __hc32 token; - - /* to avoid racing the HC, use the dummy td instead of - * the first td of our list (becomes new dummy). both - * tds stay deactivated until we're done, when the - * HC is allowed to fetch the old dummy (4.10.2). - */ - token = qtd->hw_token; - qtd->hw_token = HALT_BIT(fotg210); - - dummy = qh->dummy; - - dma = dummy->qtd_dma; - *dummy = *qtd; - dummy->qtd_dma = dma; - - list_del(&qtd->qtd_list); - list_add(&dummy->qtd_list, qtd_list); - list_splice_tail(qtd_list, &qh->qtd_list); - - fotg210_qtd_init(fotg210, qtd, qtd->qtd_dma); - qh->dummy = qtd; - - /* hc must see the new dummy at list end */ - dma = qtd->qtd_dma; - qtd = list_entry(qh->qtd_list.prev, - struct fotg210_qtd, qtd_list); - qtd->hw_next = QTD_NEXT(fotg210, dma); - - /* let the hc process these next qtds */ - wmb(); - dummy->hw_token = token; - - urb->hcpriv = qh; - } - } - return qh; -} - -/*-------------------------------------------------------------------------*/ - -static int -submit_async( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *qtd_list, - gfp_t mem_flags -) { - int epnum; - unsigned long flags; - struct fotg210_qh *qh = NULL; - int rc; - - epnum = urb->ep->desc.bEndpointAddress; - -#ifdef FOTG210_URB_TRACE - { - struct fotg210_qtd *qtd; - qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list); - fotg210_dbg(fotg210, - "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", - __func__, urb->dev->devpath, urb, - epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", - urb->transfer_buffer_length, - qtd, urb->ep->hcpriv); - } -#endif - - spin_lock_irqsave(&fotg210->lock, flags); - if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { - rc = -ESHUTDOWN; - goto done; - } - rc = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); - if (unlikely(rc)) - goto done; - - qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); - if (unlikely(qh == NULL)) { - usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); - rc = -ENOMEM; - goto done; - } - - /* Control/bulk operations through TTs don't need scheduling, - * the HC and TT handle it when the TT has a buffer ready. - */ - if (likely(qh->qh_state == QH_STATE_IDLE)) - qh_link_async(fotg210, qh); - done: - spin_unlock_irqrestore(&fotg210->lock, flags); - if (unlikely(qh == NULL)) - qtd_list_free(fotg210, urb, qtd_list); - return rc; -} - -/*-------------------------------------------------------------------------*/ - -static void single_unlink_async(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh) -{ - struct fotg210_qh *prev; - - /* Add to the end of the list of QHs waiting for the next IAAD */ - qh->qh_state = QH_STATE_UNLINK; - if (fotg210->async_unlink) - fotg210->async_unlink_last->unlink_next = qh; - else - fotg210->async_unlink = qh; - fotg210->async_unlink_last = qh; - - /* Unlink it from the schedule */ - prev = fotg210->async; - while (prev->qh_next.qh != qh) - prev = prev->qh_next.qh; - - prev->hw->hw_next = qh->hw->hw_next; - prev->qh_next = qh->qh_next; - if (fotg210->qh_scan_next == qh) - fotg210->qh_scan_next = qh->qh_next.qh; -} - -static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) -{ - /* - * Do nothing if an IAA cycle is already running or - * if one will be started shortly. - */ - if (fotg210->async_iaa || fotg210->async_unlinking) - return; - - /* Do all the waiting QHs at once */ - fotg210->async_iaa = fotg210->async_unlink; - fotg210->async_unlink = NULL; - - /* If the controller isn't running, we don't have to wait for it */ - if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) { - if (!nested) /* Avoid recursion */ - end_unlink_async(fotg210); - - /* Otherwise start a new IAA cycle */ - } else if (likely(fotg210->rh_state == FOTG210_RH_RUNNING)) { - /* Make sure the unlinks are all visible to the hardware */ - wmb(); - - fotg210_writel(fotg210, fotg210->command | CMD_IAAD, - &fotg210->regs->command); - fotg210_readl(fotg210, &fotg210->regs->command); - fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG, - true); - } -} - -/* the async qh for the qtds being unlinked are now gone from the HC */ - -static void end_unlink_async(struct fotg210_hcd *fotg210) -{ - struct fotg210_qh *qh; - - /* Process the idle QHs */ - restart: - fotg210->async_unlinking = true; - while (fotg210->async_iaa) { - qh = fotg210->async_iaa; - fotg210->async_iaa = qh->unlink_next; - qh->unlink_next = NULL; - - qh->qh_state = QH_STATE_IDLE; - qh->qh_next.qh = NULL; - - qh_completions(fotg210, qh); - if (!list_empty(&qh->qtd_list) && - fotg210->rh_state == FOTG210_RH_RUNNING) - qh_link_async(fotg210, qh); - disable_async(fotg210); - } - fotg210->async_unlinking = false; - - /* Start a new IAA cycle if any QHs are waiting for it */ - if (fotg210->async_unlink) { - start_iaa_cycle(fotg210, true); - if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) - goto restart; - } -} - -static void unlink_empty_async(struct fotg210_hcd *fotg210) -{ - struct fotg210_qh *qh, *next; - bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); - bool check_unlinks_later = false; - - /* Unlink all the async QHs that have been empty for a timer cycle */ - next = fotg210->async->qh_next.qh; - while (next) { - qh = next; - next = qh->qh_next.qh; - - if (list_empty(&qh->qtd_list) && - qh->qh_state == QH_STATE_LINKED) { - if (!stopped && qh->unlink_cycle == - fotg210->async_unlink_cycle) - check_unlinks_later = true; - else - single_unlink_async(fotg210, qh); - } - } - - /* Start a new IAA cycle if any QHs are waiting for it */ - if (fotg210->async_unlink) - start_iaa_cycle(fotg210, false); - - /* QHs that haven't been empty for long enough will be handled later */ - if (check_unlinks_later) { - fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS, - true); - ++fotg210->async_unlink_cycle; - } -} - -/* makes sure the async qh will become idle */ -/* caller must own fotg210->lock */ - -static void start_unlink_async(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh) -{ - /* - * If the QH isn't linked then there's nothing we can do - * unless we were called during a giveback, in which case - * qh_completions() has to deal with it. - */ - if (qh->qh_state != QH_STATE_LINKED) { - if (qh->qh_state == QH_STATE_COMPLETING) - qh->needs_rescan = 1; - return; - } - - single_unlink_async(fotg210, qh); - start_iaa_cycle(fotg210, false); -} - -/*-------------------------------------------------------------------------*/ - -static void scan_async(struct fotg210_hcd *fotg210) -{ - struct fotg210_qh *qh; - bool check_unlinks_later = false; - - fotg210->qh_scan_next = fotg210->async->qh_next.qh; - while (fotg210->qh_scan_next) { - qh = fotg210->qh_scan_next; - fotg210->qh_scan_next = qh->qh_next.qh; - rescan: - /* clean any finished work for this qh */ - if (!list_empty(&qh->qtd_list)) { - int temp; - - /* - * Unlinks could happen here; completion reporting - * drops the lock. That's why fotg210->qh_scan_next - * always holds the next qh to scan; if the next qh - * gets unlinked then fotg210->qh_scan_next is adjusted - * in single_unlink_async(). - */ - temp = qh_completions(fotg210, qh); - if (qh->needs_rescan) { - start_unlink_async(fotg210, qh); - } else if (list_empty(&qh->qtd_list) - && qh->qh_state == QH_STATE_LINKED) { - qh->unlink_cycle = fotg210->async_unlink_cycle; - check_unlinks_later = true; - } else if (temp != 0) - goto rescan; - } - } - - /* - * Unlink empty entries, reducing DMA usage as well - * as HCD schedule-scanning costs. Delay for any qh - * we just scanned, there's a not-unusual case that it - * doesn't stay idle for long. - */ - if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING && - !(fotg210->enabled_hrtimer_events & - BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { - fotg210_enable_event(fotg210, - FOTG210_HRTIMER_ASYNC_UNLINKS, true); - ++fotg210->async_unlink_cycle; - } -} -/*-------------------------------------------------------------------------*/ -/* - * EHCI scheduled transaction support: interrupt, iso, split iso - * These are called "periodic" transactions in the EHCI spec. - * - * Note that for interrupt transfers, the QH/QTD manipulation is shared - * with the "asynchronous" transaction support (control/bulk transfers). - * The only real difference is in how interrupt transfers are scheduled. - * - * For ISO, we make an "iso_stream" head to serve the same role as a QH. - * It keeps track of every ITD (or SITD) that's linked, and holds enough - * pre-calculated schedule data to make appending to the queue be quick. - */ - -static int fotg210_get_frame(struct usb_hcd *hcd); - -/*-------------------------------------------------------------------------*/ - -/* - * periodic_next_shadow - return "next" pointer on shadow list - * @periodic: host pointer to qh/itd - * @tag: hardware tag for type of this record - */ -static union fotg210_shadow * -periodic_next_shadow(struct fotg210_hcd *fotg210, - union fotg210_shadow *periodic, __hc32 tag) -{ - switch (hc32_to_cpu(fotg210, tag)) { - case Q_TYPE_QH: - return &periodic->qh->qh_next; - case Q_TYPE_FSTN: - return &periodic->fstn->fstn_next; - default: - return &periodic->itd->itd_next; - } -} - -static __hc32 * -shadow_next_periodic(struct fotg210_hcd *fotg210, - union fotg210_shadow *periodic, __hc32 tag) -{ - switch (hc32_to_cpu(fotg210, tag)) { - /* our fotg210_shadow.qh is actually software part */ - case Q_TYPE_QH: - return &periodic->qh->hw->hw_next; - /* others are hw parts */ - default: - return periodic->hw_next; - } -} - -/* caller must hold fotg210->lock */ -static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, - void *ptr) -{ - union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; - __hc32 *hw_p = &fotg210->periodic[frame]; - union fotg210_shadow here = *prev_p; - - /* find predecessor of "ptr"; hw and shadow lists are in sync */ - while (here.ptr && here.ptr != ptr) { - prev_p = periodic_next_shadow(fotg210, prev_p, - Q_NEXT_TYPE(fotg210, *hw_p)); - hw_p = shadow_next_periodic(fotg210, &here, - Q_NEXT_TYPE(fotg210, *hw_p)); - here = *prev_p; - } - /* an interrupt entry (at list end) could have been shared */ - if (!here.ptr) - return; - - /* update shadow and hardware lists ... the old "next" pointers - * from ptr may still be in use, the caller updates them. - */ - *prev_p = *periodic_next_shadow(fotg210, &here, - Q_NEXT_TYPE(fotg210, *hw_p)); - - *hw_p = *shadow_next_periodic(fotg210, &here, - Q_NEXT_TYPE(fotg210, *hw_p)); -} - -/* how many of the uframe's 125 usecs are allocated? */ -static unsigned short -periodic_usecs(struct fotg210_hcd *fotg210, unsigned frame, unsigned uframe) -{ - __hc32 *hw_p = &fotg210->periodic[frame]; - union fotg210_shadow *q = &fotg210->pshadow[frame]; - unsigned usecs = 0; - struct fotg210_qh_hw *hw; - - while (q->ptr) { - switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) { - case Q_TYPE_QH: - hw = q->qh->hw; - /* is it in the S-mask? */ - if (hw->hw_info2 & cpu_to_hc32(fotg210, 1 << uframe)) - usecs += q->qh->usecs; - /* ... or C-mask? */ - if (hw->hw_info2 & cpu_to_hc32(fotg210, - 1 << (8 + uframe))) - usecs += q->qh->c_usecs; - hw_p = &hw->hw_next; - q = &q->qh->qh_next; - break; - /* case Q_TYPE_FSTN: */ - default: - /* for "save place" FSTNs, count the relevant INTR - * bandwidth from the previous frame - */ - if (q->fstn->hw_prev != FOTG210_LIST_END(fotg210)) - fotg210_dbg(fotg210, "ignoring FSTN cost ...\n"); - - hw_p = &q->fstn->hw_next; - q = &q->fstn->fstn_next; - break; - case Q_TYPE_ITD: - if (q->itd->hw_transaction[uframe]) - usecs += q->itd->stream->usecs; - hw_p = &q->itd->hw_next; - q = &q->itd->itd_next; - break; - } - } -#ifdef DEBUG - if (usecs > fotg210->uframe_periodic_max) - fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", - frame * 8 + uframe, usecs); -#endif - return usecs; -} - -/*-------------------------------------------------------------------------*/ - -static int same_tt(struct usb_device *dev1, struct usb_device *dev2) -{ - if (!dev1->tt || !dev2->tt) - return 0; - if (dev1->tt != dev2->tt) - return 0; - if (dev1->tt->multi) - return dev1->ttport == dev2->ttport; - else - return 1; -} - -/* return true iff the device's transaction translator is available - * for a periodic transfer starting at the specified frame, using - * all the uframes in the mask. - */ -static int tt_no_collision( - struct fotg210_hcd *fotg210, - unsigned period, - struct usb_device *dev, - unsigned frame, - u32 uf_mask -) -{ - if (period == 0) /* error */ - return 0; - - /* note bandwidth wastage: split never follows csplit - * (different dev or endpoint) until the next uframe. - * calling convention doesn't make that distinction. - */ - for (; frame < fotg210->periodic_size; frame += period) { - union fotg210_shadow here; - __hc32 type; - struct fotg210_qh_hw *hw; - - here = fotg210->pshadow[frame]; - type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]); - while (here.ptr) { - switch (hc32_to_cpu(fotg210, type)) { - case Q_TYPE_ITD: - type = Q_NEXT_TYPE(fotg210, here.itd->hw_next); - here = here.itd->itd_next; - continue; - case Q_TYPE_QH: - hw = here.qh->hw; - if (same_tt(dev, here.qh->dev)) { - u32 mask; - - mask = hc32_to_cpu(fotg210, - hw->hw_info2); - /* "knows" no gap is needed */ - mask |= mask >> 8; - if (mask & uf_mask) - break; - } - type = Q_NEXT_TYPE(fotg210, hw->hw_next); - here = here.qh->qh_next; - continue; - /* case Q_TYPE_FSTN: */ - default: - fotg210_dbg(fotg210, - "periodic frame %d bogus type %d\n", - frame, type); - } - - /* collision or error */ - return 0; - } - } - - /* no collision */ - return 1; -} - -/*-------------------------------------------------------------------------*/ - -static void enable_periodic(struct fotg210_hcd *fotg210) -{ - if (fotg210->periodic_count++) - return; - - /* Stop waiting to turn off the periodic schedule */ - fotg210->enabled_hrtimer_events &= - ~BIT(FOTG210_HRTIMER_DISABLE_PERIODIC); - - /* Don't start the schedule until PSS is 0 */ - fotg210_poll_PSS(fotg210); - turn_on_io_watchdog(fotg210); -} - -static void disable_periodic(struct fotg210_hcd *fotg210) -{ - if (--fotg210->periodic_count) - return; - - /* Don't turn off the schedule until PSS is 1 */ - fotg210_poll_PSS(fotg210); -} - -/*-------------------------------------------------------------------------*/ - -/* periodic schedule slots have iso tds (normal or split) first, then a - * sparse tree for active interrupt transfers. - * - * this just links in a qh; caller guarantees uframe masks are set right. - * no FSTN support (yet; fotg210 0.96+) - */ -static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{ - unsigned i; - unsigned period = qh->period; - - dev_dbg(&qh->dev->dev, - "link qh%d-%04x/%p start %d [%d/%d us]\n", - period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) - & (QH_CMASK | QH_SMASK), - qh, qh->start, qh->usecs, qh->c_usecs); - - /* high bandwidth, or otherwise every microframe */ - if (period == 0) - period = 1; - - for (i = qh->start; i < fotg210->periodic_size; i += period) { - union fotg210_shadow *prev = &fotg210->pshadow[i]; - __hc32 *hw_p = &fotg210->periodic[i]; - union fotg210_shadow here = *prev; - __hc32 type = 0; - - /* skip the iso nodes at list head */ - while (here.ptr) { - type = Q_NEXT_TYPE(fotg210, *hw_p); - if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) - break; - prev = periodic_next_shadow(fotg210, prev, type); - hw_p = shadow_next_periodic(fotg210, &here, type); - here = *prev; - } - - /* sorting each branch by period (slow-->fast) - * enables sharing interior tree nodes - */ - while (here.ptr && qh != here.qh) { - if (qh->period > here.qh->period) - break; - prev = &here.qh->qh_next; - hw_p = &here.qh->hw->hw_next; - here = *prev; - } - /* link in this qh, unless some earlier pass did that */ - if (qh != here.qh) { - qh->qh_next = here; - if (here.qh) - qh->hw->hw_next = *hw_p; - wmb(); - prev->qh = qh; - *hw_p = QH_NEXT(fotg210, qh->qh_dma); - } - } - qh->qh_state = QH_STATE_LINKED; - qh->xacterrs = 0; - - /* update per-qh bandwidth for usbfs */ - fotg210_to_hcd(fotg210)->self.bandwidth_allocated += qh->period - ? ((qh->usecs + qh->c_usecs) / qh->period) - : (qh->usecs * 8); - - list_add(&qh->intr_node, &fotg210->intr_qh_list); - - /* maybe enable periodic schedule processing */ - ++fotg210->intr_count; - enable_periodic(fotg210); -} - -static void qh_unlink_periodic(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh) -{ - unsigned i; - unsigned period; - - /* - * If qh is for a low/full-speed device, simply unlinking it - * could interfere with an ongoing split transaction. To unlink - * it safely would require setting the QH_INACTIVATE bit and - * waiting at least one frame, as described in EHCI 4.12.2.5. - * - * We won't bother with any of this. Instead, we assume that the - * only reason for unlinking an interrupt QH while the current URB - * is still active is to dequeue all the URBs (flush the whole - * endpoint queue). - * - * If rebalancing the periodic schedule is ever implemented, this - * approach will no longer be valid. - */ - - /* high bandwidth, or otherwise part of every microframe */ - period = qh->period; - if (!period) - period = 1; - - for (i = qh->start; i < fotg210->periodic_size; i += period) - periodic_unlink(fotg210, i, qh); - - /* update per-qh bandwidth for usbfs */ - fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= qh->period - ? ((qh->usecs + qh->c_usecs) / qh->period) - : (qh->usecs * 8); - - dev_dbg(&qh->dev->dev, - "unlink qh%d-%04x/%p start %d [%d/%d us]\n", - qh->period, - hc32_to_cpup(fotg210, &qh->hw->hw_info2) & - (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); - - /* qh->qh_next still "live" to HC */ - qh->qh_state = QH_STATE_UNLINK; - qh->qh_next.ptr = NULL; - - if (fotg210->qh_scan_next == qh) - fotg210->qh_scan_next = list_entry(qh->intr_node.next, - struct fotg210_qh, intr_node); - list_del(&qh->intr_node); -} - -static void start_unlink_intr(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh) -{ - /* If the QH isn't linked then there's nothing we can do - * unless we were called during a giveback, in which case - * qh_completions() has to deal with it. - */ - if (qh->qh_state != QH_STATE_LINKED) { - if (qh->qh_state == QH_STATE_COMPLETING) - qh->needs_rescan = 1; - return; - } - - qh_unlink_periodic(fotg210, qh); - - /* Make sure the unlinks are visible before starting the timer */ - wmb(); - - /* - * The EHCI spec doesn't say how long it takes the controller to - * stop accessing an unlinked interrupt QH. The timer delay is - * 9 uframes; presumably that will be long enough. - */ - qh->unlink_cycle = fotg210->intr_unlink_cycle; - - /* New entries go at the end of the intr_unlink list */ - if (fotg210->intr_unlink) - fotg210->intr_unlink_last->unlink_next = qh; - else - fotg210->intr_unlink = qh; - fotg210->intr_unlink_last = qh; - - if (fotg210->intr_unlinking) - ; /* Avoid recursive calls */ - else if (fotg210->rh_state < FOTG210_RH_RUNNING) - fotg210_handle_intr_unlinks(fotg210); - else if (fotg210->intr_unlink == qh) { - fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, - true); - ++fotg210->intr_unlink_cycle; - } -} - -static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{ - struct fotg210_qh_hw *hw = qh->hw; - int rc; - - qh->qh_state = QH_STATE_IDLE; - hw->hw_next = FOTG210_LIST_END(fotg210); - - qh_completions(fotg210, qh); - - /* reschedule QH iff another request is queued */ - if (!list_empty(&qh->qtd_list) && - fotg210->rh_state == FOTG210_RH_RUNNING) { - rc = qh_schedule(fotg210, qh); - - /* An error here likely indicates handshake failure - * or no space left in the schedule. Neither fault - * should happen often ... - * - * FIXME kill the now-dysfunctional queued urbs - */ - if (rc != 0) - fotg210_err(fotg210, "can't reschedule qh %p, err %d\n", - qh, rc); - } - - /* maybe turn off periodic schedule */ - --fotg210->intr_count; - disable_periodic(fotg210); -} - -/*-------------------------------------------------------------------------*/ - -static int check_period( - struct fotg210_hcd *fotg210, - unsigned frame, - unsigned uframe, - unsigned period, - unsigned usecs -) { - int claimed; - - /* complete split running into next frame? - * given FSTN support, we could sometimes check... - */ - if (uframe >= 8) - return 0; - - /* convert "usecs we need" to "max already claimed" */ - usecs = fotg210->uframe_periodic_max - usecs; - - /* we "know" 2 and 4 uframe intervals were rejected; so - * for period 0, check _every_ microframe in the schedule. - */ - if (unlikely(period == 0)) { - do { - for (uframe = 0; uframe < 7; uframe++) { - claimed = periodic_usecs(fotg210, frame, - uframe); - if (claimed > usecs) - return 0; - } - } while ((frame += 1) < fotg210->periodic_size); - - /* just check the specified uframe, at that period */ - } else { - do { - claimed = periodic_usecs(fotg210, frame, uframe); - if (claimed > usecs) - return 0; - } while ((frame += period) < fotg210->periodic_size); - } - - /* success! */ - return 1; -} - -static int check_intr_schedule( - struct fotg210_hcd *fotg210, - unsigned frame, - unsigned uframe, - const struct fotg210_qh *qh, - __hc32 *c_maskp -) -{ - int retval = -ENOSPC; - u8 mask = 0; - - if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ - goto done; - - if (!check_period(fotg210, frame, uframe, qh->period, qh->usecs)) - goto done; - if (!qh->c_usecs) { - retval = 0; - *c_maskp = 0; - goto done; - } - - /* Make sure this tt's buffer is also available for CSPLITs. - * We pessimize a bit; probably the typical full speed case - * doesn't need the second CSPLIT. - * - * NOTE: both SPLIT and CSPLIT could be checked in just - * one smart pass... - */ - mask = 0x03 << (uframe + qh->gap_uf); - *c_maskp = cpu_to_hc32(fotg210, mask << 8); - - mask |= 1 << uframe; - if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) { - if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1, - qh->period, qh->c_usecs)) - goto done; - if (!check_period(fotg210, frame, uframe + qh->gap_uf, - qh->period, qh->c_usecs)) - goto done; - retval = 0; - } -done: - return retval; -} - -/* "first fit" scheduling policy used the first time through, - * or when the previous schedule slot can't be re-used. - */ -static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{ - int status; - unsigned uframe; - __hc32 c_mask; - unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ - struct fotg210_qh_hw *hw = qh->hw; - - qh_refresh(fotg210, qh); - hw->hw_next = FOTG210_LIST_END(fotg210); - frame = qh->start; - - /* reuse the previous schedule slots, if we can */ - if (frame < qh->period) { - uframe = ffs(hc32_to_cpup(fotg210, &hw->hw_info2) & QH_SMASK); - status = check_intr_schedule(fotg210, frame, --uframe, - qh, &c_mask); - } else { - uframe = 0; - c_mask = 0; - status = -ENOSPC; - } - - /* else scan the schedule to find a group of slots such that all - * uframes have enough periodic bandwidth available. - */ - if (status) { - /* "normal" case, uframing flexible except with splits */ - if (qh->period) { - int i; - - for (i = qh->period; status && i > 0; --i) { - frame = ++fotg210->random_frame % qh->period; - for (uframe = 0; uframe < 8; uframe++) { - status = check_intr_schedule(fotg210, - frame, uframe, qh, - &c_mask); - if (status == 0) - break; - } - } - - /* qh->period == 0 means every uframe */ - } else { - frame = 0; - status = check_intr_schedule(fotg210, 0, 0, qh, - &c_mask); - } - if (status) - goto done; - qh->start = frame; - - /* reset S-frame and (maybe) C-frame masks */ - hw->hw_info2 &= cpu_to_hc32(fotg210, ~(QH_CMASK | QH_SMASK)); - hw->hw_info2 |= qh->period - ? cpu_to_hc32(fotg210, 1 << uframe) - : cpu_to_hc32(fotg210, QH_SMASK); - hw->hw_info2 |= c_mask; - } else - fotg210_dbg(fotg210, "reused qh %p schedule\n", qh); - - /* stuff into the periodic schedule */ - qh_link_periodic(fotg210, qh); -done: - return status; -} - -static int intr_submit( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *qtd_list, - gfp_t mem_flags -) { - unsigned epnum; - unsigned long flags; - struct fotg210_qh *qh; - int status; - struct list_head empty; - - /* get endpoint and transfer/schedule data */ - epnum = urb->ep->desc.bEndpointAddress; - - spin_lock_irqsave(&fotg210->lock, flags); - - if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { - status = -ESHUTDOWN; - goto done_not_linked; - } - status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); - if (unlikely(status)) - goto done_not_linked; - - /* get qh and force any scheduling errors */ - INIT_LIST_HEAD(&empty); - qh = qh_append_tds(fotg210, urb, &empty, epnum, &urb->ep->hcpriv); - if (qh == NULL) { - status = -ENOMEM; - goto done; - } - if (qh->qh_state == QH_STATE_IDLE) { - status = qh_schedule(fotg210, qh); - if (status) - goto done; - } - - /* then queue the urb's tds to the qh */ - qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); - BUG_ON(qh == NULL); - - /* ... update usbfs periodic stats */ - fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs++; - -done: - if (unlikely(status)) - usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); -done_not_linked: - spin_unlock_irqrestore(&fotg210->lock, flags); - if (status) - qtd_list_free(fotg210, urb, qtd_list); - - return status; -} - -static void scan_intr(struct fotg210_hcd *fotg210) -{ - struct fotg210_qh *qh; - - list_for_each_entry_safe(qh, fotg210->qh_scan_next, - &fotg210->intr_qh_list, intr_node) { - rescan: - /* clean any finished work for this qh */ - if (!list_empty(&qh->qtd_list)) { - int temp; - - /* - * Unlinks could happen here; completion reporting - * drops the lock. That's why fotg210->qh_scan_next - * always holds the next qh to scan; if the next qh - * gets unlinked then fotg210->qh_scan_next is adjusted - * in qh_unlink_periodic(). - */ - temp = qh_completions(fotg210, qh); - if (unlikely(qh->needs_rescan || - (list_empty(&qh->qtd_list) && - qh->qh_state == QH_STATE_LINKED))) - start_unlink_intr(fotg210, qh); - else if (temp != 0) - goto rescan; - } - } -} - -/*-------------------------------------------------------------------------*/ - -/* fotg210_iso_stream ops work with both ITD and SITD */ - -static struct fotg210_iso_stream * -iso_stream_alloc(gfp_t mem_flags) -{ - struct fotg210_iso_stream *stream; - - stream = kzalloc(sizeof(*stream), mem_flags); - if (likely(stream != NULL)) { - INIT_LIST_HEAD(&stream->td_list); - INIT_LIST_HEAD(&stream->free_list); - stream->next_uframe = -1; - } - return stream; -} - -static void -iso_stream_init( - struct fotg210_hcd *fotg210, - struct fotg210_iso_stream *stream, - struct usb_device *dev, - int pipe, - unsigned interval -) -{ - u32 buf1; - unsigned epnum, maxp; - int is_input; - long bandwidth; - unsigned multi; - - /* - * this might be a "high bandwidth" highspeed endpoint, - * as encoded in the ep descriptor's wMaxPacket field - */ - epnum = usb_pipeendpoint(pipe); - is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; - maxp = usb_maxpacket(dev, pipe, !is_input); - if (is_input) - buf1 = (1 << 11); - else - buf1 = 0; - - maxp = max_packet(maxp); - multi = hb_mult(maxp); - buf1 |= maxp; - maxp *= multi; - - stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); - stream->buf1 = cpu_to_hc32(fotg210, buf1); - stream->buf2 = cpu_to_hc32(fotg210, multi); - - /* usbfs wants to report the average usecs per frame tied up - * when transfers on this endpoint are scheduled ... - */ - if (dev->speed == USB_SPEED_FULL) { - interval <<= 3; - stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, - is_input, 1, maxp)); - stream->usecs /= 8; - } else { - stream->highspeed = 1; - stream->usecs = HS_USECS_ISO(maxp); - } - bandwidth = stream->usecs * 8; - bandwidth /= interval; - - stream->bandwidth = bandwidth; - stream->udev = dev; - stream->bEndpointAddress = is_input | epnum; - stream->interval = interval; - stream->maxp = maxp; -} - -static struct fotg210_iso_stream * -iso_stream_find(struct fotg210_hcd *fotg210, struct urb *urb) -{ - unsigned epnum; - struct fotg210_iso_stream *stream; - struct usb_host_endpoint *ep; - unsigned long flags; - - epnum = usb_pipeendpoint(urb->pipe); - if (usb_pipein(urb->pipe)) - ep = urb->dev->ep_in[epnum]; - else - ep = urb->dev->ep_out[epnum]; - - spin_lock_irqsave(&fotg210->lock, flags); - stream = ep->hcpriv; - - if (unlikely(stream == NULL)) { - stream = iso_stream_alloc(GFP_ATOMIC); - if (likely(stream != NULL)) { - ep->hcpriv = stream; - stream->ep = ep; - iso_stream_init(fotg210, stream, urb->dev, urb->pipe, - urb->interval); - } - - /* if dev->ep[epnum] is a QH, hw is set */ - } else if (unlikely(stream->hw != NULL)) { - fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n", - urb->dev->devpath, epnum, - usb_pipein(urb->pipe) ? "in" : "out"); - stream = NULL; - } - - spin_unlock_irqrestore(&fotg210->lock, flags); - return stream; -} - -/*-------------------------------------------------------------------------*/ - -/* fotg210_iso_sched ops can be ITD-only or SITD-only */ - -static struct fotg210_iso_sched * -iso_sched_alloc(unsigned packets, gfp_t mem_flags) -{ - struct fotg210_iso_sched *iso_sched; - int size = sizeof(*iso_sched); - - size += packets * sizeof(struct fotg210_iso_packet); - iso_sched = kzalloc(size, mem_flags); - if (likely(iso_sched != NULL)) - INIT_LIST_HEAD(&iso_sched->td_list); - - return iso_sched; -} - -static inline void -itd_sched_init( - struct fotg210_hcd *fotg210, - struct fotg210_iso_sched *iso_sched, - struct fotg210_iso_stream *stream, - struct urb *urb -) -{ - unsigned i; - dma_addr_t dma = urb->transfer_dma; - - /* how many uframes are needed for these transfers */ - iso_sched->span = urb->number_of_packets * stream->interval; - - /* figure out per-uframe itd fields that we'll need later - * when we fit new itds into the schedule. - */ - for (i = 0; i < urb->number_of_packets; i++) { - struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; - unsigned length; - dma_addr_t buf; - u32 trans; - - length = urb->iso_frame_desc[i].length; - buf = dma + urb->iso_frame_desc[i].offset; - - trans = FOTG210_ISOC_ACTIVE; - trans |= buf & 0x0fff; - if (unlikely(((i + 1) == urb->number_of_packets)) - && !(urb->transfer_flags & URB_NO_INTERRUPT)) - trans |= FOTG210_ITD_IOC; - trans |= length << 16; - uframe->transaction = cpu_to_hc32(fotg210, trans); - - /* might need to cross a buffer page within a uframe */ - uframe->bufp = (buf & ~(u64)0x0fff); - buf += length; - if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff)))) - uframe->cross = 1; - } -} - -static void -iso_sched_free( - struct fotg210_iso_stream *stream, - struct fotg210_iso_sched *iso_sched -) -{ - if (!iso_sched) - return; - /* caller must hold fotg210->lock!*/ - list_splice(&iso_sched->td_list, &stream->free_list); - kfree(iso_sched); -} - -static int -itd_urb_transaction( - struct fotg210_iso_stream *stream, - struct fotg210_hcd *fotg210, - struct urb *urb, - gfp_t mem_flags -) -{ - struct fotg210_itd *itd; - dma_addr_t itd_dma; - int i; - unsigned num_itds; - struct fotg210_iso_sched *sched; - unsigned long flags; - - sched = iso_sched_alloc(urb->number_of_packets, mem_flags); - if (unlikely(sched == NULL)) - return -ENOMEM; - - itd_sched_init(fotg210, sched, stream, urb); - - if (urb->interval < 8) - num_itds = 1 + (sched->span + 7) / 8; - else - num_itds = urb->number_of_packets; - - /* allocate/init ITDs */ - spin_lock_irqsave(&fotg210->lock, flags); - for (i = 0; i < num_itds; i++) { - - /* - * Use iTDs from the free list, but not iTDs that may - * still be in use by the hardware. - */ - if (likely(!list_empty(&stream->free_list))) { - itd = list_first_entry(&stream->free_list, - struct fotg210_itd, itd_list); - if (itd->frame == fotg210->now_frame) - goto alloc_itd; - list_del(&itd->itd_list); - itd_dma = itd->itd_dma; - } else { - alloc_itd: - spin_unlock_irqrestore(&fotg210->lock, flags); - itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, - &itd_dma); - spin_lock_irqsave(&fotg210->lock, flags); - if (!itd) { - iso_sched_free(stream, sched); - spin_unlock_irqrestore(&fotg210->lock, flags); - return -ENOMEM; - } - } - - memset(itd, 0, sizeof(*itd)); - itd->itd_dma = itd_dma; - list_add(&itd->itd_list, &sched->td_list); - } - spin_unlock_irqrestore(&fotg210->lock, flags); - - /* temporarily store schedule info in hcpriv */ - urb->hcpriv = sched; - urb->error_count = 0; - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static inline int -itd_slot_ok( - struct fotg210_hcd *fotg210, - u32 mod, - u32 uframe, - u8 usecs, - u32 period -) -{ - uframe %= period; - do { - /* can't commit more than uframe_periodic_max usec */ - if (periodic_usecs(fotg210, uframe >> 3, uframe & 0x7) - > (fotg210->uframe_periodic_max - usecs)) - return 0; - - /* we know urb->interval is 2^N uframes */ - uframe += period; - } while (uframe < mod); - return 1; -} - -/* - * This scheduler plans almost as far into the future as it has actual - * periodic schedule slots. (Affected by TUNE_FLS, which defaults to - * "as small as possible" to be cache-friendlier.) That limits the size - * transfers you can stream reliably; avoid more than 64 msec per urb. - * Also avoid queue depths of less than fotg210's worst irq latency (affected - * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter, - * and other factors); or more than about 230 msec total (for portability, - * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler! - */ - -#define SCHEDULE_SLOP 80 /* microframes */ - -static int -iso_stream_schedule( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct fotg210_iso_stream *stream -) -{ - u32 now, next, start, period, span; - int status; - unsigned mod = fotg210->periodic_size << 3; - struct fotg210_iso_sched *sched = urb->hcpriv; - - period = urb->interval; - span = sched->span; - - if (span > mod - SCHEDULE_SLOP) { - fotg210_dbg(fotg210, "iso request %p too long\n", urb); - status = -EFBIG; - goto fail; - } - - now = fotg210_read_frame_index(fotg210) & (mod - 1); - - /* Typical case: reuse current schedule, stream is still active. - * Hopefully there are no gaps from the host falling behind - * (irq delays etc), but if there are we'll take the next - * slot in the schedule, implicitly assuming URB_ISO_ASAP. - */ - if (likely(!list_empty(&stream->td_list))) { - u32 excess; - - /* For high speed devices, allow scheduling within the - * isochronous scheduling threshold. For full speed devices - * and Intel PCI-based controllers, don't (work around for - * Intel ICH9 bug). - */ - if (!stream->highspeed && fotg210->fs_i_thresh) - next = now + fotg210->i_thresh; - else - next = now; - - /* Fell behind (by up to twice the slop amount)? - * We decide based on the time of the last currently-scheduled - * slot, not the time of the next available slot. - */ - excess = (stream->next_uframe - period - next) & (mod - 1); - if (excess >= mod - 2 * SCHEDULE_SLOP) - start = next + excess - mod + period * - DIV_ROUND_UP(mod - excess, period); - else - start = next + excess + period; - if (start - now >= mod) { - fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", - urb, start - now - period, period, - mod); - status = -EFBIG; - goto fail; - } - } - - /* need to schedule; when's the next (u)frame we could start? - * this is bigger than fotg210->i_thresh allows; scheduling itself - * isn't free, the slop should handle reasonably slow cpus. it - * can also help high bandwidth if the dma and irq loads don't - * jump until after the queue is primed. - */ - else { - int done = 0; - start = SCHEDULE_SLOP + (now & ~0x07); - - /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ - - /* find a uframe slot with enough bandwidth. - * Early uframes are more precious because full-speed - * iso IN transfers can't use late uframes, - * and therefore they should be allocated last. - */ - next = start; - start += period; - do { - start--; - /* check schedule: enough space? */ - if (itd_slot_ok(fotg210, mod, start, - stream->usecs, period)) - done = 1; - } while (start > next && !done); - - /* no room in the schedule */ - if (!done) { - fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n", - urb, now, now + mod); - status = -ENOSPC; - goto fail; - } - } - - /* Tried to schedule too far into the future? */ - if (unlikely(start - now + span - period - >= mod - 2 * SCHEDULE_SLOP)) { - fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", - urb, start - now, span - period, - mod - 2 * SCHEDULE_SLOP); - status = -EFBIG; - goto fail; - } - - stream->next_uframe = start & (mod - 1); - - /* report high speed start in uframes; full speed, in frames */ - urb->start_frame = stream->next_uframe; - if (!stream->highspeed) - urb->start_frame >>= 3; - - /* Make sure scan_isoc() sees these */ - if (fotg210->isoc_count == 0) - fotg210->next_frame = now >> 3; - return 0; - - fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; - return status; -} - -/*-------------------------------------------------------------------------*/ - -static inline void -itd_init(struct fotg210_hcd *fotg210, struct fotg210_iso_stream *stream, - struct fotg210_itd *itd) -{ - int i; - - /* it's been recently zeroed */ - itd->hw_next = FOTG210_LIST_END(fotg210); - itd->hw_bufp[0] = stream->buf0; - itd->hw_bufp[1] = stream->buf1; - itd->hw_bufp[2] = stream->buf2; - - for (i = 0; i < 8; i++) - itd->index[i] = -1; - - /* All other fields are filled when scheduling */ -} - -static inline void -itd_patch( - struct fotg210_hcd *fotg210, - struct fotg210_itd *itd, - struct fotg210_iso_sched *iso_sched, - unsigned index, - u16 uframe -) -{ - struct fotg210_iso_packet *uf = &iso_sched->packet[index]; - unsigned pg = itd->pg; - - uframe &= 0x07; - itd->index[uframe] = index; - - itd->hw_transaction[uframe] = uf->transaction; - itd->hw_transaction[uframe] |= cpu_to_hc32(fotg210, pg << 12); - itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, uf->bufp & ~(u32)0); - itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(uf->bufp >> 32)); - - /* iso_frame_desc[].offset must be strictly increasing */ - if (unlikely(uf->cross)) { - u64 bufp = uf->bufp + 4096; - - itd->pg = ++pg; - itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0); - itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(bufp >> 32)); - } -} - -static inline void -itd_link(struct fotg210_hcd *fotg210, unsigned frame, struct fotg210_itd *itd) -{ - union fotg210_shadow *prev = &fotg210->pshadow[frame]; - __hc32 *hw_p = &fotg210->periodic[frame]; - union fotg210_shadow here = *prev; - __hc32 type = 0; - - /* skip any iso nodes which might belong to previous microframes */ - while (here.ptr) { - type = Q_NEXT_TYPE(fotg210, *hw_p); - if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) - break; - prev = periodic_next_shadow(fotg210, prev, type); - hw_p = shadow_next_periodic(fotg210, &here, type); - here = *prev; - } - - itd->itd_next = here; - itd->hw_next = *hw_p; - prev->itd = itd; - itd->frame = frame; - wmb(); - *hw_p = cpu_to_hc32(fotg210, itd->itd_dma | Q_TYPE_ITD); -} - -/* fit urb's itds into the selected schedule slot; activate as needed */ -static void itd_link_urb( - struct fotg210_hcd *fotg210, - struct urb *urb, - unsigned mod, - struct fotg210_iso_stream *stream -) -{ - int packet; - unsigned next_uframe, uframe, frame; - struct fotg210_iso_sched *iso_sched = urb->hcpriv; - struct fotg210_itd *itd; - - next_uframe = stream->next_uframe & (mod - 1); - - if (unlikely(list_empty(&stream->td_list))) { - fotg210_to_hcd(fotg210)->self.bandwidth_allocated - += stream->bandwidth; - fotg210_vdbg(fotg210, - "schedule devp %s ep%d%s-iso period %d start %d.%d\n", - urb->dev->devpath, stream->bEndpointAddress & 0x0f, - (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", - urb->interval, - next_uframe >> 3, next_uframe & 0x7); - } - - /* fill iTDs uframe by uframe */ - for (packet = 0, itd = NULL; packet < urb->number_of_packets;) { - if (itd == NULL) { - /* ASSERT: we have all necessary itds */ - - /* ASSERT: no itds for this endpoint in this uframe */ - - itd = list_entry(iso_sched->td_list.next, - struct fotg210_itd, itd_list); - list_move_tail(&itd->itd_list, &stream->td_list); - itd->stream = stream; - itd->urb = urb; - itd_init(fotg210, stream, itd); - } - - uframe = next_uframe & 0x07; - frame = next_uframe >> 3; - - itd_patch(fotg210, itd, iso_sched, packet, uframe); - - next_uframe += stream->interval; - next_uframe &= mod - 1; - packet++; - - /* link completed itds into the schedule */ - if (((next_uframe >> 3) != frame) - || packet == urb->number_of_packets) { - itd_link(fotg210, frame & (fotg210->periodic_size - 1), - itd); - itd = NULL; - } - } - stream->next_uframe = next_uframe; - - /* don't need that schedule data any more */ - iso_sched_free(stream, iso_sched); - urb->hcpriv = NULL; - - ++fotg210->isoc_count; - enable_periodic(fotg210); -} - -#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ - FOTG210_ISOC_XACTERR) - -/* Process and recycle a completed ITD. Return true iff its urb completed, - * and hence its completion callback probably added things to the hardware - * schedule. - * - * Note that we carefully avoid recycling this descriptor until after any - * completion callback runs, so that it won't be reused quickly. That is, - * assuming (a) no more than two urbs per frame on this endpoint, and also - * (b) only this endpoint's completions submit URBs. It seems some silicon - * corrupts things if you reuse completed descriptors very quickly... - */ -static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) -{ - struct urb *urb = itd->urb; - struct usb_iso_packet_descriptor *desc; - u32 t; - unsigned uframe; - int urb_index = -1; - struct fotg210_iso_stream *stream = itd->stream; - struct usb_device *dev; - bool retval = false; - - /* for each uframe with a packet */ - for (uframe = 0; uframe < 8; uframe++) { - if (likely(itd->index[uframe] == -1)) - continue; - urb_index = itd->index[uframe]; - desc = &urb->iso_frame_desc[urb_index]; - - t = hc32_to_cpup(fotg210, &itd->hw_transaction[uframe]); - itd->hw_transaction[uframe] = 0; - - /* report transfer status */ - if (unlikely(t & ISO_ERRS)) { - urb->error_count++; - if (t & FOTG210_ISOC_BUF_ERR) - desc->status = usb_pipein(urb->pipe) - ? -ENOSR /* hc couldn't read */ - : -ECOMM; /* hc couldn't write */ - else if (t & FOTG210_ISOC_BABBLE) - desc->status = -EOVERFLOW; - else /* (t & FOTG210_ISOC_XACTERR) */ - desc->status = -EPROTO; - - /* HC need not update length with this error */ - if (!(t & FOTG210_ISOC_BABBLE)) { - desc->actual_length = - fotg210_itdlen(urb, desc, t); - urb->actual_length += desc->actual_length; - } - } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { - desc->status = 0; - desc->actual_length = fotg210_itdlen(urb, desc, t); - urb->actual_length += desc->actual_length; - } else { - /* URB was too late */ - desc->status = -EXDEV; - } - } - - /* handle completion now? */ - if (likely((urb_index + 1) != urb->number_of_packets)) - goto done; - - /* ASSERT: it's really the last itd for this urb - list_for_each_entry (itd, &stream->td_list, itd_list) - BUG_ON (itd->urb == urb); - */ - - /* give urb back to the driver; completion often (re)submits */ - dev = urb->dev; - fotg210_urb_done(fotg210, urb, 0); - retval = true; - urb = NULL; - - --fotg210->isoc_count; - disable_periodic(fotg210); - - if (unlikely(list_is_singular(&stream->td_list))) { - fotg210_to_hcd(fotg210)->self.bandwidth_allocated - -= stream->bandwidth; - fotg210_vdbg(fotg210, - "deschedule devp %s ep%d%s-iso\n", - dev->devpath, stream->bEndpointAddress & 0x0f, - (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); - } - -done: - itd->urb = NULL; - - /* Add to the end of the free list for later reuse */ - list_move_tail(&itd->itd_list, &stream->free_list); - - /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ - if (list_empty(&stream->td_list)) { - list_splice_tail_init(&stream->free_list, - &fotg210->cached_itd_list); - start_free_itds(fotg210); - } - - return retval; -} - -/*-------------------------------------------------------------------------*/ - -static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, - gfp_t mem_flags) -{ - int status = -EINVAL; - unsigned long flags; - struct fotg210_iso_stream *stream; - - /* Get iso_stream head */ - stream = iso_stream_find(fotg210, urb); - if (unlikely(stream == NULL)) { - fotg210_dbg(fotg210, "can't get iso stream\n"); - return -ENOMEM; - } - if (unlikely(urb->interval != stream->interval && - fotg210_port_speed(fotg210, 0) == - USB_PORT_STAT_HIGH_SPEED)) { - fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", - stream->interval, urb->interval); - goto done; - } - -#ifdef FOTG210_URB_TRACE - fotg210_dbg(fotg210, - "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", - __func__, urb->dev->devpath, urb, - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out", - urb->transfer_buffer_length, - urb->number_of_packets, urb->interval, - stream); -#endif - - /* allocate ITDs w/o locking anything */ - status = itd_urb_transaction(stream, fotg210, urb, mem_flags); - if (unlikely(status < 0)) { - fotg210_dbg(fotg210, "can't init itds\n"); - goto done; - } - - /* schedule ... need to lock */ - spin_lock_irqsave(&fotg210->lock, flags); - if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { - status = -ESHUTDOWN; - goto done_not_linked; - } - status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); - if (unlikely(status)) - goto done_not_linked; - status = iso_stream_schedule(fotg210, urb, stream); - if (likely(status == 0)) - itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream); - else - usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); - done_not_linked: - spin_unlock_irqrestore(&fotg210->lock, flags); - done: - return status; -} - -/*-------------------------------------------------------------------------*/ - -static void scan_isoc(struct fotg210_hcd *fotg210) -{ - unsigned uf, now_frame, frame; - unsigned fmask = fotg210->periodic_size - 1; - bool modified, live; - - /* - * When running, scan from last scan point up to "now" - * else clean up by scanning everything that's left. - * Touches as few pages as possible: cache-friendly. - */ - if (fotg210->rh_state >= FOTG210_RH_RUNNING) { - uf = fotg210_read_frame_index(fotg210); - now_frame = (uf >> 3) & fmask; - live = true; - } else { - now_frame = (fotg210->next_frame - 1) & fmask; - live = false; - } - fotg210->now_frame = now_frame; - - frame = fotg210->next_frame; - for (;;) { - union fotg210_shadow q, *q_p; - __hc32 type, *hw_p; - -restart: - /* scan each element in frame's queue for completions */ - q_p = &fotg210->pshadow[frame]; - hw_p = &fotg210->periodic[frame]; - q.ptr = q_p->ptr; - type = Q_NEXT_TYPE(fotg210, *hw_p); - modified = false; - - while (q.ptr != NULL) { - switch (hc32_to_cpu(fotg210, type)) { - case Q_TYPE_ITD: - /* If this ITD is still active, leave it for - * later processing ... check the next entry. - * No need to check for activity unless the - * frame is current. - */ - if (frame == now_frame && live) { - rmb(); - for (uf = 0; uf < 8; uf++) { - if (q.itd->hw_transaction[uf] & - ITD_ACTIVE(fotg210)) - break; - } - if (uf < 8) { - q_p = &q.itd->itd_next; - hw_p = &q.itd->hw_next; - type = Q_NEXT_TYPE(fotg210, - q.itd->hw_next); - q = *q_p; - break; - } - } - - /* Take finished ITDs out of the schedule - * and process them: recycle, maybe report - * URB completion. HC won't cache the - * pointer for much longer, if at all. - */ - *q_p = q.itd->itd_next; - *hw_p = q.itd->hw_next; - type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); - wmb(); - modified = itd_complete(fotg210, q.itd); - q = *q_p; - break; - default: - fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", - type, frame, q.ptr); - /* FALL THROUGH */ - case Q_TYPE_QH: - case Q_TYPE_FSTN: - /* End of the iTDs and siTDs */ - q.ptr = NULL; - break; - } - - /* assume completion callbacks modify the queue */ - if (unlikely(modified && fotg210->isoc_count > 0)) - goto restart; - } - - /* Stop when we have reached the current frame */ - if (frame == now_frame) - break; - frame = (frame + 1) & fmask; - } - fotg210->next_frame = now_frame; -} -/*-------------------------------------------------------------------------*/ -/* - * Display / Set uframe_periodic_max - */ -static ssize_t show_uframe_periodic_max(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct fotg210_hcd *fotg210; - int n; - - fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); - n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); - return n; -} - - -static ssize_t store_uframe_periodic_max(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct fotg210_hcd *fotg210; - unsigned uframe_periodic_max; - unsigned frame, uframe; - unsigned short allocated_max; - unsigned long flags; - ssize_t ret; - - fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); - if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) - return -EINVAL; - - if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { - fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n", - uframe_periodic_max); - return -EINVAL; - } - - ret = -EINVAL; - - /* - * lock, so that our checking does not race with possible periodic - * bandwidth allocation through submitting new urbs. - */ - spin_lock_irqsave(&fotg210->lock, flags); - - /* - * for request to decrease max periodic bandwidth, we have to check - * every microframe in the schedule to see whether the decrease is - * possible. - */ - if (uframe_periodic_max < fotg210->uframe_periodic_max) { - allocated_max = 0; - - for (frame = 0; frame < fotg210->periodic_size; ++frame) - for (uframe = 0; uframe < 7; ++uframe) - allocated_max = max(allocated_max, - periodic_usecs(fotg210, frame, uframe)); - - if (allocated_max > uframe_periodic_max) { - fotg210_info(fotg210, - "cannot decrease uframe_periodic_max becase " - "periodic bandwidth is already allocated " - "(%u > %u)\n", - allocated_max, uframe_periodic_max); - goto out_unlock; - } - } - - /* increasing is always ok */ - - fotg210_info(fotg210, "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", - 100 * uframe_periodic_max/125, uframe_periodic_max); - - if (uframe_periodic_max != 100) - fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n"); - - fotg210->uframe_periodic_max = uframe_periodic_max; - ret = count; - -out_unlock: - spin_unlock_irqrestore(&fotg210->lock, flags); - return ret; -} - -static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, - store_uframe_periodic_max); - -static inline int create_sysfs_files(struct fotg210_hcd *fotg210) -{ - struct device *controller = fotg210_to_hcd(fotg210)->self.controller; - int i = 0; - - if (i) - goto out; - - i = device_create_file(controller, &dev_attr_uframe_periodic_max); -out: - return i; -} - -static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) -{ - struct device *controller = fotg210_to_hcd(fotg210)->self.controller; - - device_remove_file(controller, &dev_attr_uframe_periodic_max); -} -/*-------------------------------------------------------------------------*/ - -/* On some systems, leaving remote wakeup enabled prevents system shutdown. - * The firmware seems to think that powering off is a wakeup event! - * This routine turns off remote wakeup and everything else, on all ports. - */ -static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210) -{ - u32 __iomem *status_reg = &fotg210->regs->port_status; - - fotg210_writel(fotg210, PORT_RWC_BITS, status_reg); -} - -/* - * Halt HC, turn off all ports, and let the BIOS use the companion controllers. - * Must be called with interrupts enabled and the lock not held. - */ -static void fotg210_silence_controller(struct fotg210_hcd *fotg210) -{ - fotg210_halt(fotg210); - - spin_lock_irq(&fotg210->lock); - fotg210->rh_state = FOTG210_RH_HALTED; - fotg210_turn_off_all_ports(fotg210); - spin_unlock_irq(&fotg210->lock); -} - -/* fotg210_shutdown kick in for silicon on any bus (not just pci, etc). - * This forcibly disables dma and IRQs, helping kexec and other cases - * where the next system software may expect clean state. - */ -static void fotg210_shutdown(struct usb_hcd *hcd) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - - spin_lock_irq(&fotg210->lock); - fotg210->shutdown = true; - fotg210->rh_state = FOTG210_RH_STOPPING; - fotg210->enabled_hrtimer_events = 0; - spin_unlock_irq(&fotg210->lock); - - fotg210_silence_controller(fotg210); - - hrtimer_cancel(&fotg210->hrtimer); -} - -/*-------------------------------------------------------------------------*/ - -/* - * fotg210_work is called from some interrupts, timers, and so on. - * it calls driver completion functions, after dropping fotg210->lock. - */ -static void fotg210_work(struct fotg210_hcd *fotg210) -{ - /* another CPU may drop fotg210->lock during a schedule scan while - * it reports urb completions. this flag guards against bogus - * attempts at re-entrant schedule scanning. - */ - if (fotg210->scanning) { - fotg210->need_rescan = true; - return; - } - fotg210->scanning = true; - - rescan: - fotg210->need_rescan = false; - if (fotg210->async_count) - scan_async(fotg210); - if (fotg210->intr_count > 0) - scan_intr(fotg210); - if (fotg210->isoc_count > 0) - scan_isoc(fotg210); - if (fotg210->need_rescan) - goto rescan; - fotg210->scanning = false; - - /* the IO watchdog guards against hardware or driver bugs that - * misplace IRQs, and should let us run completely without IRQs. - * such lossage has been observed on both VT6202 and VT8235. - */ - turn_on_io_watchdog(fotg210); -} - -/* - * Called when the fotg210_hcd module is removed. - */ -static void fotg210_stop(struct usb_hcd *hcd) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - - fotg210_dbg(fotg210, "stop\n"); - - /* no more interrupts ... */ - - spin_lock_irq(&fotg210->lock); - fotg210->enabled_hrtimer_events = 0; - spin_unlock_irq(&fotg210->lock); - - fotg210_quiesce(fotg210); - fotg210_silence_controller(fotg210); - fotg210_reset(fotg210); - - hrtimer_cancel(&fotg210->hrtimer); - remove_sysfs_files(fotg210); - remove_debug_files(fotg210); - - /* root hub is shut down separately (first, when possible) */ - spin_lock_irq(&fotg210->lock); - end_free_itds(fotg210); - spin_unlock_irq(&fotg210->lock); - fotg210_mem_cleanup(fotg210); - -#ifdef FOTG210_STATS - fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n", - fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa, - fotg210->stats.lost_iaa); - fotg210_dbg(fotg210, "complete %ld unlink %ld\n", - fotg210->stats.complete, fotg210->stats.unlink); -#endif - - dbg_status(fotg210, "fotg210_stop completed", - fotg210_readl(fotg210, &fotg210->regs->status)); -} - -/* one-time init, only for memory state */ -static int hcd_fotg210_init(struct usb_hcd *hcd) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - u32 temp; - int retval; - u32 hcc_params; - struct fotg210_qh_hw *hw; - - spin_lock_init(&fotg210->lock); - - /* - * keep io watchdog by default, those good HCDs could turn off it later - */ - fotg210->need_io_watchdog = 1; - - hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - fotg210->hrtimer.function = fotg210_hrtimer_func; - fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; - - hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); - - /* - * by default set standard 80% (== 100 usec/uframe) max periodic - * bandwidth as required by USB 2.0 - */ - fotg210->uframe_periodic_max = 100; - - /* - * hw default: 1K periodic list heads, one per frame. - * periodic_size can shrink by USBCMD update if hcc_params allows. - */ - fotg210->periodic_size = DEFAULT_I_TDPS; - INIT_LIST_HEAD(&fotg210->intr_qh_list); - INIT_LIST_HEAD(&fotg210->cached_itd_list); - - if (HCC_PGM_FRAMELISTLEN(hcc_params)) { - /* periodic schedule size can be smaller than default */ - switch (FOTG210_TUNE_FLS) { - case 0: - fotg210->periodic_size = 1024; - break; - case 1: - fotg210->periodic_size = 512; - break; - case 2: - fotg210->periodic_size = 256; - break; - default: - BUG(); - } - } - retval = fotg210_mem_init(fotg210, GFP_KERNEL); - if (retval < 0) - return retval; - - /* controllers may cache some of the periodic schedule ... */ - fotg210->i_thresh = 2; - - /* - * dedicate a qh for the async ring head, since we couldn't unlink - * a 'real' qh without stopping the async schedule [4.8]. use it - * as the 'reclamation list head' too. - * its dummy is used in hw_alt_next of many tds, to prevent the qh - * from automatically advancing to the next td after short reads. - */ - fotg210->async->qh_next.qh = NULL; - hw = fotg210->async->hw; - hw->hw_next = QH_NEXT(fotg210, fotg210->async->qh_dma); - hw->hw_info1 = cpu_to_hc32(fotg210, QH_HEAD); - hw->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); - hw->hw_qtd_next = FOTG210_LIST_END(fotg210); - fotg210->async->qh_state = QH_STATE_LINKED; - hw->hw_alt_next = QTD_NEXT(fotg210, fotg210->async->dummy->qtd_dma); - - /* clear interrupt enables, set irq latency */ - if (log2_irq_thresh < 0 || log2_irq_thresh > 6) - log2_irq_thresh = 0; - temp = 1 << (16 + log2_irq_thresh); - if (HCC_CANPARK(hcc_params)) { - /* HW default park == 3, on hardware that supports it (like - * NVidia and ALI silicon), maximizes throughput on the async - * schedule by avoiding QH fetches between transfers. - * - * With fast usb storage devices and NForce2, "park" seems to - * make problems: throughput reduction (!), data errors... - */ - if (park) { - park = min_t(unsigned, park, 3); - temp |= CMD_PARK; - temp |= park << 8; - } - fotg210_dbg(fotg210, "park %d\n", park); - } - if (HCC_PGM_FRAMELISTLEN(hcc_params)) { - /* periodic schedule size can be smaller than default */ - temp &= ~(3 << 2); - temp |= (FOTG210_TUNE_FLS << 2); - } - fotg210->command = temp; - - /* Accept arbitrarily long scatter-gather lists */ - if (!(hcd->driver->flags & HCD_LOCAL_MEM)) - hcd->self.sg_tablesize = ~0; - return 0; -} - -/* start HC running; it's halted, hcd_fotg210_init() has been run (once) */ -static int fotg210_run(struct usb_hcd *hcd) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - u32 temp; - u32 hcc_params; - - hcd->uses_new_polling = 1; - - /* EHCI spec section 4.1 */ - - fotg210_writel(fotg210, fotg210->periodic_dma, - &fotg210->regs->frame_list); - fotg210_writel(fotg210, (u32)fotg210->async->qh_dma, - &fotg210->regs->async_next); - - /* - * hcc_params controls whether fotg210->regs->segment must (!!!) - * be used; it constrains QH/ITD/SITD and QTD locations. - * pci_pool consistent memory always uses segment zero. - * streaming mappings for I/O buffers, like pci_map_single(), - * can return segments above 4GB, if the device allows. - * - * NOTE: the dma mask is visible through dma_supported(), so - * drivers can pass this info along ... like NETIF_F_HIGHDMA, - * Scsi_Host.highmem_io, and so forth. It's readonly to all - * host side drivers though. - */ - hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); - - /* - * Philips, Intel, and maybe others need CMD_RUN before the - * root hub will detect new devices (why?); NEC doesn't - */ - fotg210->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); - fotg210->command |= CMD_RUN; - fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); - dbg_cmd(fotg210, "init", fotg210->command); - - /* - * Start, enabling full USB 2.0 functionality ... usb 1.1 devices - * are explicitly handed to companion controller(s), so no TT is - * involved with the root hub. (Except where one is integrated, - * and there's no companion controller unless maybe for USB OTG.) - * - * Turning on the CF flag will transfer ownership of all ports - * from the companions to the EHCI controller. If any of the - * companions are in the middle of a port reset at the time, it - * could cause trouble. Write-locking ehci_cf_port_reset_rwsem - * guarantees that no resets are in progress. After we set CF, - * a short delay lets the hardware catch up; new resets shouldn't - * be started before the port switching actions could complete. - */ - down_write(&ehci_cf_port_reset_rwsem); - fotg210->rh_state = FOTG210_RH_RUNNING; - /* unblock posted writes */ - fotg210_readl(fotg210, &fotg210->regs->command); - msleep(5); - up_write(&ehci_cf_port_reset_rwsem); - fotg210->last_periodic_enable = ktime_get_real(); - - temp = HC_VERSION(fotg210, - fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); - fotg210_info(fotg210, - "USB %x.%x started, EHCI %x.%02x\n", - ((fotg210->sbrn & 0xf0)>>4), (fotg210->sbrn & 0x0f), - temp >> 8, temp & 0xff); - - fotg210_writel(fotg210, INTR_MASK, - &fotg210->regs->intr_enable); /* Turn On Interrupts */ - - /* GRR this is run-once init(), being done every time the HC starts. - * So long as they're part of class devices, we can't do it init() - * since the class device isn't created that early. - */ - create_debug_files(fotg210); - create_sysfs_files(fotg210); - - return 0; -} - -static int fotg210_setup(struct usb_hcd *hcd) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - int retval; - - fotg210->regs = (void __iomem *)fotg210->caps + - HC_LENGTH(fotg210, - fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); - dbg_hcs_params(fotg210, "reset"); - dbg_hcc_params(fotg210, "reset"); - - /* cache this readonly data; minimize chip reads */ - fotg210->hcs_params = fotg210_readl(fotg210, - &fotg210->caps->hcs_params); - - fotg210->sbrn = HCD_USB2; - - /* data structure init */ - retval = hcd_fotg210_init(hcd); - if (retval) - return retval; - - retval = fotg210_halt(fotg210); - if (retval) - return retval; - - fotg210_reset(fotg210); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static irqreturn_t fotg210_irq(struct usb_hcd *hcd) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - u32 status, masked_status, pcd_status = 0, cmd; - int bh; - - spin_lock(&fotg210->lock); - - status = fotg210_readl(fotg210, &fotg210->regs->status); - - /* e.g. cardbus physical eject */ - if (status == ~(u32) 0) { - fotg210_dbg(fotg210, "device removed\n"); - goto dead; - } - - /* - * We don't use STS_FLR, but some controllers don't like it to - * remain on, so mask it out along with the other status bits. - */ - masked_status = status & (INTR_MASK | STS_FLR); - - /* Shared IRQ? */ - if (!masked_status || - unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { - spin_unlock(&fotg210->lock); - return IRQ_NONE; - } - - /* clear (just) interrupts */ - fotg210_writel(fotg210, masked_status, &fotg210->regs->status); - cmd = fotg210_readl(fotg210, &fotg210->regs->command); - bh = 0; - -#ifdef VERBOSE_DEBUG - /* unrequested/ignored: Frame List Rollover */ - dbg_status(fotg210, "irq", status); -#endif - - /* INT, ERR, and IAA interrupt rates can be throttled */ - - /* normal [4.15.1.2] or error [4.15.1.1] completion */ - if (likely((status & (STS_INT|STS_ERR)) != 0)) { - if (likely((status & STS_ERR) == 0)) - COUNT(fotg210->stats.normal); - else - COUNT(fotg210->stats.error); - bh = 1; - } - - /* complete the unlinking of some qh [4.15.2.3] */ - if (status & STS_IAA) { - - /* Turn off the IAA watchdog */ - fotg210->enabled_hrtimer_events &= - ~BIT(FOTG210_HRTIMER_IAA_WATCHDOG); - - /* - * Mild optimization: Allow another IAAD to reset the - * hrtimer, if one occurs before the next expiration. - * In theory we could always cancel the hrtimer, but - * tests show that about half the time it will be reset - * for some other event anyway. - */ - if (fotg210->next_hrtimer_event == FOTG210_HRTIMER_IAA_WATCHDOG) - ++fotg210->next_hrtimer_event; - - /* guard against (alleged) silicon errata */ - if (cmd & CMD_IAAD) - fotg210_dbg(fotg210, "IAA with IAAD still set?\n"); - if (fotg210->async_iaa) { - COUNT(fotg210->stats.iaa); - end_unlink_async(fotg210); - } else - fotg210_dbg(fotg210, "IAA with nothing unlinked?\n"); - } - - /* remote wakeup [4.3.1] */ - if (status & STS_PCD) { - int pstatus; - u32 __iomem *status_reg = &fotg210->regs->port_status; - - /* kick root hub later */ - pcd_status = status; - - /* resume root hub? */ - if (fotg210->rh_state == FOTG210_RH_SUSPENDED) - usb_hcd_resume_root_hub(hcd); - - pstatus = fotg210_readl(fotg210, status_reg); - - if (test_bit(0, &fotg210->suspended_ports) && - ((pstatus & PORT_RESUME) || - !(pstatus & PORT_SUSPEND)) && - (pstatus & PORT_PE) && - fotg210->reset_done[0] == 0) { - - /* start 20 msec resume signaling from this port, - * and make khubd collect PORT_STAT_C_SUSPEND to - * stop that signaling. Use 5 ms extra for safety, - * like usb_port_resume() does. - */ - fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25); - set_bit(0, &fotg210->resuming_ports); - fotg210_dbg(fotg210, "port 1 remote wakeup\n"); - mod_timer(&hcd->rh_timer, fotg210->reset_done[0]); - } - } - - /* PCI errors [4.15.2.4] */ - if (unlikely((status & STS_FATAL) != 0)) { - fotg210_err(fotg210, "fatal error\n"); - dbg_cmd(fotg210, "fatal", cmd); - dbg_status(fotg210, "fatal", status); -dead: - usb_hc_died(hcd); - - /* Don't let the controller do anything more */ - fotg210->shutdown = true; - fotg210->rh_state = FOTG210_RH_STOPPING; - fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); - fotg210_writel(fotg210, fotg210->command, - &fotg210->regs->command); - fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); - fotg210_handle_controller_death(fotg210); - - /* Handle completions when the controller stops */ - bh = 0; - } - - if (bh) - fotg210_work(fotg210); - spin_unlock(&fotg210->lock); - if (pcd_status) - usb_hcd_poll_rh_status(hcd); - return IRQ_HANDLED; -} - -/*-------------------------------------------------------------------------*/ - -/* - * non-error returns are a promise to giveback() the urb later - * we drop ownership so next owner (or urb unlink) can get it - * - * urb + dev is in hcd.self.controller.urb_list - * we're queueing TDs onto software and hardware lists - * - * hcd-specific init for hcpriv hasn't been done yet - * - * NOTE: control, bulk, and interrupt share the same code to append TDs - * to a (possibly active) QH, and the same QH scanning code. - */ -static int fotg210_urb_enqueue( - struct usb_hcd *hcd, - struct urb *urb, - gfp_t mem_flags -) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - struct list_head qtd_list; - - INIT_LIST_HEAD(&qtd_list); - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - /* qh_completions() code doesn't handle all the fault cases - * in multi-TD control transfers. Even 1KB is rare anyway. - */ - if (urb->transfer_buffer_length > (16 * 1024)) - return -EMSGSIZE; - /* FALLTHROUGH */ - /* case PIPE_BULK: */ - default: - if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) - return -ENOMEM; - return submit_async(fotg210, urb, &qtd_list, mem_flags); - - case PIPE_INTERRUPT: - if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) - return -ENOMEM; - return intr_submit(fotg210, urb, &qtd_list, mem_flags); - - case PIPE_ISOCHRONOUS: - return itd_submit(fotg210, urb, mem_flags); - } -} - -/* remove from hardware lists - * completions normally happen asynchronously - */ - -static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - struct fotg210_qh *qh; - unsigned long flags; - int rc; - - spin_lock_irqsave(&fotg210->lock, flags); - rc = usb_hcd_check_unlink_urb(hcd, urb, status); - if (rc) - goto done; - - switch (usb_pipetype(urb->pipe)) { - /* case PIPE_CONTROL: */ - /* case PIPE_BULK:*/ - default: - qh = (struct fotg210_qh *) urb->hcpriv; - if (!qh) - break; - switch (qh->qh_state) { - case QH_STATE_LINKED: - case QH_STATE_COMPLETING: - start_unlink_async(fotg210, qh); - break; - case QH_STATE_UNLINK: - case QH_STATE_UNLINK_WAIT: - /* already started */ - break; - case QH_STATE_IDLE: - /* QH might be waiting for a Clear-TT-Buffer */ - qh_completions(fotg210, qh); - break; - } - break; - - case PIPE_INTERRUPT: - qh = (struct fotg210_qh *) urb->hcpriv; - if (!qh) - break; - switch (qh->qh_state) { - case QH_STATE_LINKED: - case QH_STATE_COMPLETING: - start_unlink_intr(fotg210, qh); - break; - case QH_STATE_IDLE: - qh_completions(fotg210, qh); - break; - default: - fotg210_dbg(fotg210, "bogus qh %p state %d\n", - qh, qh->qh_state); - goto done; - } - break; - - case PIPE_ISOCHRONOUS: - /* itd... */ - - /* wait till next completion, do it then. */ - /* completion irqs can wait up to 1024 msec, */ - break; - } -done: - spin_unlock_irqrestore(&fotg210->lock, flags); - return rc; -} - -/*-------------------------------------------------------------------------*/ - -/* bulk qh holds the data toggle */ - -static void -fotg210_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - unsigned long flags; - struct fotg210_qh *qh, *tmp; - - /* ASSERT: any requests/urbs are being unlinked */ - /* ASSERT: nobody can be submitting urbs for this any more */ - -rescan: - spin_lock_irqsave(&fotg210->lock, flags); - qh = ep->hcpriv; - if (!qh) - goto done; - - /* endpoints can be iso streams. for now, we don't - * accelerate iso completions ... so spin a while. - */ - if (qh->hw == NULL) { - struct fotg210_iso_stream *stream = ep->hcpriv; - - if (!list_empty(&stream->td_list)) - goto idle_timeout; - - /* BUG_ON(!list_empty(&stream->free_list)); */ - kfree(stream); - goto done; - } - - if (fotg210->rh_state < FOTG210_RH_RUNNING) - qh->qh_state = QH_STATE_IDLE; - switch (qh->qh_state) { - case QH_STATE_LINKED: - case QH_STATE_COMPLETING: - for (tmp = fotg210->async->qh_next.qh; - tmp && tmp != qh; - tmp = tmp->qh_next.qh) - continue; - /* periodic qh self-unlinks on empty, and a COMPLETING qh - * may already be unlinked. - */ - if (tmp) - start_unlink_async(fotg210, qh); - /* FALL THROUGH */ - case QH_STATE_UNLINK: /* wait for hw to finish? */ - case QH_STATE_UNLINK_WAIT: -idle_timeout: - spin_unlock_irqrestore(&fotg210->lock, flags); - schedule_timeout_uninterruptible(1); - goto rescan; - case QH_STATE_IDLE: /* fully unlinked */ - if (qh->clearing_tt) - goto idle_timeout; - if (list_empty(&qh->qtd_list)) { - qh_destroy(fotg210, qh); - break; - } - /* else FALL THROUGH */ - default: - /* caller was supposed to have unlinked any requests; - * that's not our job. just leak this memory. - */ - fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n", - qh, ep->desc.bEndpointAddress, qh->qh_state, - list_empty(&qh->qtd_list) ? "" : "(has tds)"); - break; - } - done: - ep->hcpriv = NULL; - spin_unlock_irqrestore(&fotg210->lock, flags); -} - -static void -fotg210_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - struct fotg210_qh *qh; - int eptype = usb_endpoint_type(&ep->desc); - int epnum = usb_endpoint_num(&ep->desc); - int is_out = usb_endpoint_dir_out(&ep->desc); - unsigned long flags; - - if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) - return; - - spin_lock_irqsave(&fotg210->lock, flags); - qh = ep->hcpriv; - - /* For Bulk and Interrupt endpoints we maintain the toggle state - * in the hardware; the toggle bits in udev aren't used at all. - * When an endpoint is reset by usb_clear_halt() we must reset - * the toggle bit in the QH. - */ - if (qh) { - usb_settoggle(qh->dev, epnum, is_out, 0); - if (!list_empty(&qh->qtd_list)) { - WARN_ONCE(1, "clear_halt for a busy endpoint\n"); - } else if (qh->qh_state == QH_STATE_LINKED || - qh->qh_state == QH_STATE_COMPLETING) { - - /* The toggle value in the QH can't be updated - * while the QH is active. Unlink it now; - * re-linking will call qh_refresh(). - */ - if (eptype == USB_ENDPOINT_XFER_BULK) - start_unlink_async(fotg210, qh); - else - start_unlink_intr(fotg210, qh); - } - } - spin_unlock_irqrestore(&fotg210->lock, flags); -} - -static int fotg210_get_frame(struct usb_hcd *hcd) -{ - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - return (fotg210_read_frame_index(fotg210) >> 3) % - fotg210->periodic_size; -} - -/*-------------------------------------------------------------------------*/ - -/* - * The EHCI in ChipIdea HDRC cannot be a separate module or device, - * because its registers (and irq) are shared between host/gadget/otg - * functions and in order to facilitate role switching we cannot - * give the fotg210 driver exclusive access to those. - */ -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_LICENSE("GPL"); - -static const struct hc_driver fotg210_fotg210_hc_driver = { - .description = hcd_name, - .product_desc = "Faraday USB2.0 Host Controller", - .hcd_priv_size = sizeof(struct fotg210_hcd), - - /* - * generic hardware linkage - */ - .irq = fotg210_irq, - .flags = HCD_MEMORY | HCD_USB2, - - /* - * basic lifecycle operations - */ - .reset = hcd_fotg210_init, - .start = fotg210_run, - .stop = fotg210_stop, - .shutdown = fotg210_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = fotg210_urb_enqueue, - .urb_dequeue = fotg210_urb_dequeue, - .endpoint_disable = fotg210_endpoint_disable, - .endpoint_reset = fotg210_endpoint_reset, - - /* - * scheduling support - */ - .get_frame_number = fotg210_get_frame, - - /* - * root hub support - */ - .hub_status_data = fotg210_hub_status_data, - .hub_control = fotg210_hub_control, - .bus_suspend = fotg210_bus_suspend, - .bus_resume = fotg210_bus_resume, - - .relinquish_port = fotg210_relinquish_port, - .port_handed_over = fotg210_port_handed_over, - - .clear_tt_buffer_complete = fotg210_clear_tt_buffer_complete, -}; - -static void fotg210_init(struct fotg210_hcd *fotg210) -{ - u32 value; - - iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, - &fotg210->regs->gmir); - - value = ioread32(&fotg210->regs->otgcsr); - value &= ~OTGCSR_A_BUS_DROP; - value |= OTGCSR_A_BUS_REQ; - iowrite32(value, &fotg210->regs->otgcsr); -} - -/** - * fotg210_hcd_probe - initialize faraday FOTG210 HCDs - * - * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - */ -static int fotg210_hcd_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct usb_hcd *hcd; - struct resource *res; - int irq; - int retval = -ENODEV; - struct fotg210_hcd *fotg210; - - if (usb_disabled()) - return -ENODEV; - - pdev->dev.power.power_state = PMSG_ON; - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, - "Found HC with no IRQ. Check %s setup!\n", - dev_name(dev)); - return -ENODEV; - } - - irq = res->start; - - hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, - dev_name(dev)); - if (!hcd) { - dev_err(dev, "failed to create hcd with err %d\n", retval); - retval = -ENOMEM; - goto fail_create_hcd; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; - } - - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - hcd->has_tt = 1; - - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - fotg210_fotg210_hc_driver.description)) { - dev_dbg(dev, "controller already in use\n"); - retval = -EBUSY; - goto fail_request_resource; - } - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; - } - - hcd->regs = ioremap_nocache(res->start, resource_size(res)); - if (hcd->regs == NULL) { - dev_dbg(dev, "error mapping memory\n"); - retval = -EFAULT; - goto fail_ioremap; - } - - fotg210 = hcd_to_fotg210(hcd); - - fotg210->caps = hcd->regs; - - retval = fotg210_setup(hcd); - if (retval) - goto fail_add_hcd; - - fotg210_init(fotg210); - - retval = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (retval) { - dev_err(dev, "failed to add hcd with err %d\n", retval); - goto fail_add_hcd; - } - - return retval; - -fail_add_hcd: - iounmap(hcd->regs); -fail_ioremap: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -fail_request_resource: - usb_put_hcd(hcd); -fail_create_hcd: - dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); - return retval; -} - -/** - * fotg210_hcd_remove - shutdown processing for EHCI HCDs - * @dev: USB Host Controller being removed - * - */ -static int fotg210_hcd_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct usb_hcd *hcd = dev_get_drvdata(dev); - - if (!hcd) - return 0; - - usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); - - return 0; -} - -static struct platform_driver fotg210_hcd_driver = { - .driver = { - .name = "fotg210-hcd", - }, - .probe = fotg210_hcd_probe, - .remove = fotg210_hcd_remove, -}; - -static int __init fotg210_hcd_init(void) -{ - int retval = 0; - - if (usb_disabled()) - return -ENODEV; - - pr_info("%s: " DRIVER_DESC "\n", hcd_name); - set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); - if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || - test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) - pr_warn(KERN_WARNING "Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); - - pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd\n", - hcd_name, - sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd), - sizeof(struct fotg210_itd)); - -#ifdef DEBUG - fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); - if (!fotg210_debug_root) { - retval = -ENOENT; - goto err_debug; - } -#endif - - retval = platform_driver_register(&fotg210_hcd_driver); - if (retval < 0) - goto clean; - return retval; - - platform_driver_unregister(&fotg210_hcd_driver); -clean: -#ifdef DEBUG - debugfs_remove(fotg210_debug_root); - fotg210_debug_root = NULL; -err_debug: -#endif - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); - return retval; -} -module_init(fotg210_hcd_init); - -static void __exit fotg210_hcd_cleanup(void) -{ - platform_driver_unregister(&fotg210_hcd_driver); -#ifdef DEBUG - debugfs_remove(fotg210_debug_root); -#endif - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); -} -module_exit(fotg210_hcd_cleanup); diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h deleted file mode 100644 index 8920f9d32564..000000000000 --- a/drivers/usb/host/fotg210.h +++ /dev/null @@ -1,750 +0,0 @@ -#ifndef __LINUX_FOTG210_H -#define __LINUX_FOTG210_H - -/* definitions used for the EHCI driver */ - -/* - * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to - * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on - * the host controller implementation. - * - * To facilitate the strongest possible byte-order checking from "sparse" - * and so on, we use __leXX unless that's not practical. - */ -#define __hc32 __le32 -#define __hc16 __le16 - -/* statistics can be kept for tuning/monitoring */ -struct fotg210_stats { - /* irq usage */ - unsigned long normal; - unsigned long error; - unsigned long iaa; - unsigned long lost_iaa; - - /* termination of urbs from core */ - unsigned long complete; - unsigned long unlink; -}; - -/* fotg210_hcd->lock guards shared data against other CPUs: - * fotg210_hcd: async, unlink, periodic (and shadow), ... - * usb_host_endpoint: hcpriv - * fotg210_qh: qh_next, qtd_list - * fotg210_qtd: qtd_list - * - * Also, hold this lock when talking to HC registers or - * when updating hw_* fields in shared qh/qtd/... structures. - */ - -#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ - -/* - * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the - * controller may be doing DMA. Lower values mean there's no DMA. - */ -enum fotg210_rh_state { - FOTG210_RH_HALTED, - FOTG210_RH_SUSPENDED, - FOTG210_RH_RUNNING, - FOTG210_RH_STOPPING -}; - -/* - * Timer events, ordered by increasing delay length. - * Always update event_delays_ns[] and event_handlers[] (defined in - * ehci-timer.c) in parallel with this list. - */ -enum fotg210_hrtimer_event { - FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */ - FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ - FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ - FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ - FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ - FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ - FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ - FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ - FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ - FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ - FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ -}; -#define FOTG210_HRTIMER_NO_EVENT 99 - -struct fotg210_hcd { /* one per controller */ - /* timing support */ - enum fotg210_hrtimer_event next_hrtimer_event; - unsigned enabled_hrtimer_events; - ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS]; - struct hrtimer hrtimer; - - int PSS_poll_count; - int ASS_poll_count; - int died_poll_count; - - /* glue to PCI and HCD framework */ - struct fotg210_caps __iomem *caps; - struct fotg210_regs __iomem *regs; - struct fotg210_dbg_port __iomem *debug; - - __u32 hcs_params; /* cached register copy */ - spinlock_t lock; - enum fotg210_rh_state rh_state; - - /* general schedule support */ - bool scanning:1; - bool need_rescan:1; - bool intr_unlinking:1; - bool async_unlinking:1; - bool shutdown:1; - struct fotg210_qh *qh_scan_next; - - /* async schedule support */ - struct fotg210_qh *async; - struct fotg210_qh *dummy; /* For AMD quirk use */ - struct fotg210_qh *async_unlink; - struct fotg210_qh *async_unlink_last; - struct fotg210_qh *async_iaa; - unsigned async_unlink_cycle; - unsigned async_count; /* async activity count */ - - /* periodic schedule support */ -#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ - unsigned periodic_size; - __hc32 *periodic; /* hw periodic table */ - dma_addr_t periodic_dma; - struct list_head intr_qh_list; - unsigned i_thresh; /* uframes HC might cache */ - - union fotg210_shadow *pshadow; /* mirror hw periodic table */ - struct fotg210_qh *intr_unlink; - struct fotg210_qh *intr_unlink_last; - unsigned intr_unlink_cycle; - unsigned now_frame; /* frame from HC hardware */ - unsigned next_frame; /* scan periodic, start here */ - unsigned intr_count; /* intr activity count */ - unsigned isoc_count; /* isoc activity count */ - unsigned periodic_count; /* periodic activity count */ - /* max periodic time per uframe */ - unsigned uframe_periodic_max; - - - /* list of itds completed while now_frame was still active */ - struct list_head cached_itd_list; - struct fotg210_itd *last_itd_to_free; - - /* per root hub port */ - unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; - - /* bit vectors (one bit per port) */ - unsigned long bus_suspended; /* which ports were - already suspended at the start of a bus suspend */ - unsigned long companion_ports; /* which ports are - dedicated to the companion controller */ - unsigned long owned_ports; /* which ports are - owned by the companion during a bus suspend */ - unsigned long port_c_suspend; /* which ports have - the change-suspend feature turned on */ - unsigned long suspended_ports; /* which ports are - suspended */ - unsigned long resuming_ports; /* which ports have - started to resume */ - - /* per-HC memory pools (could be per-bus, but ...) */ - struct dma_pool *qh_pool; /* qh per active urb */ - struct dma_pool *qtd_pool; /* one or more per qh */ - struct dma_pool *itd_pool; /* itd per iso urb */ - - unsigned random_frame; - unsigned long next_statechange; - ktime_t last_periodic_enable; - u32 command; - - /* SILICON QUIRKS */ - unsigned need_io_watchdog:1; - unsigned fs_i_thresh:1; /* Intel iso scheduling */ - - u8 sbrn; /* packed release number */ - - /* irq statistics */ -#ifdef FOTG210_STATS - struct fotg210_stats stats; -# define COUNT(x) ((x)++) -#else -# define COUNT(x) -#endif - - /* debug files */ -#ifdef DEBUG - struct dentry *debug_dir; -#endif -}; - -/* convert between an HCD pointer and the corresponding FOTG210_HCD */ -static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd) -{ - return (struct fotg210_hcd *)(hcd->hcd_priv); -} -static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210) -{ - return container_of((void *) fotg210, struct usb_hcd, hcd_priv); -} - -/*-------------------------------------------------------------------------*/ - -/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ - -/* Section 2.2 Host Controller Capability Registers */ -struct fotg210_caps { - /* these fields are specified as 8 and 16 bit registers, - * but some hosts can't perform 8 or 16 bit PCI accesses. - * some hosts treat caplength and hciversion as parts of a 32-bit - * register, others treat them as two separate registers, this - * affects the memory map for big endian controllers. - */ - u32 hc_capbase; -#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ - (fotg210_big_endian_capbase(fotg210) ? 24 : 0))) -#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ - (fotg210_big_endian_capbase(fotg210) ? 0 : 16))) - u32 hcs_params; /* HCSPARAMS - offset 0x4 */ -#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ - - u32 hcc_params; /* HCCPARAMS - offset 0x8 */ -#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ -#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ - u8 portroute[8]; /* nibbles for routing - offset 0xC */ -}; - - -/* Section 2.3 Host Controller Operational Registers */ -struct fotg210_regs { - - /* USBCMD: offset 0x00 */ - u32 command; - -/* EHCI 1.1 addendum */ -/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ -#define CMD_PARK (1<<11) /* enable "park" on async qh */ -#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ -#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ -#define CMD_ASE (1<<5) /* async schedule enable */ -#define CMD_PSE (1<<4) /* periodic schedule enable */ -/* 3:2 is periodic frame list size */ -#define CMD_RESET (1<<1) /* reset HC not bus */ -#define CMD_RUN (1<<0) /* start/stop HC */ - - /* USBSTS: offset 0x04 */ - u32 status; -#define STS_ASS (1<<15) /* Async Schedule Status */ -#define STS_PSS (1<<14) /* Periodic Schedule Status */ -#define STS_RECL (1<<13) /* Reclamation */ -#define STS_HALT (1<<12) /* Not running (any reason) */ -/* some bits reserved */ - /* these STS_* flags are also intr_enable bits (USBINTR) */ -#define STS_IAA (1<<5) /* Interrupted on async advance */ -#define STS_FATAL (1<<4) /* such as some PCI access errors */ -#define STS_FLR (1<<3) /* frame list rolled over */ -#define STS_PCD (1<<2) /* port change detect */ -#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ -#define STS_INT (1<<0) /* "normal" completion (short, ...) */ - - /* USBINTR: offset 0x08 */ - u32 intr_enable; - - /* FRINDEX: offset 0x0C */ - u32 frame_index; /* current microframe number */ - /* CTRLDSSEGMENT: offset 0x10 */ - u32 segment; /* address bits 63:32 if needed */ - /* PERIODICLISTBASE: offset 0x14 */ - u32 frame_list; /* points to periodic list */ - /* ASYNCLISTADDR: offset 0x18 */ - u32 async_next; /* address of next async queue head */ - - u32 reserved1; - /* PORTSC: offset 0x20 */ - u32 port_status; -/* 31:23 reserved */ -#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ -#define PORT_RESET (1<<8) /* reset port */ -#define PORT_SUSPEND (1<<7) /* suspend port */ -#define PORT_RESUME (1<<6) /* resume it */ -#define PORT_PEC (1<<3) /* port enable change */ -#define PORT_PE (1<<2) /* port enable */ -#define PORT_CSC (1<<1) /* connect status change */ -#define PORT_CONNECT (1<<0) /* device connected */ -#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) - u32 reserved2[19]; - - /* OTGCSR: offet 0x70 */ - u32 otgcsr; -#define OTGCSR_HOST_SPD_TYP (3 << 22) -#define OTGCSR_A_BUS_DROP (1 << 5) -#define OTGCSR_A_BUS_REQ (1 << 4) - - /* OTGISR: offset 0x74 */ - u32 otgisr; -#define OTGISR_OVC (1 << 10) - - u32 reserved3[15]; - - /* GMIR: offset 0xB4 */ - u32 gmir; -#define GMIR_INT_POLARITY (1 << 3) /*Active High*/ -#define GMIR_MHC_INT (1 << 2) -#define GMIR_MOTG_INT (1 << 1) -#define GMIR_MDEV_INT (1 << 0) -}; - -/* Appendix C, Debug port ... intended for use with special "debug devices" - * that can help if there's no serial console. (nonstandard enumeration.) - */ -struct fotg210_dbg_port { - u32 control; -#define DBGP_OWNER (1<<30) -#define DBGP_ENABLED (1<<28) -#define DBGP_DONE (1<<16) -#define DBGP_INUSE (1<<10) -#define DBGP_ERRCODE(x) (((x)>>7)&0x07) -# define DBGP_ERR_BAD 1 -# define DBGP_ERR_SIGNAL 2 -#define DBGP_ERROR (1<<6) -#define DBGP_GO (1<<5) -#define DBGP_OUT (1<<4) -#define DBGP_LEN(x) (((x)>>0)&0x0f) - u32 pids; -#define DBGP_PID_GET(x) (((x)>>16)&0xff) -#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) - u32 data03; - u32 data47; - u32 address; -#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) -}; - -#ifdef CONFIG_EARLY_PRINTK_DBGP -#include -extern int __init early_dbgp_init(char *s); -extern struct console early_dbgp_console; -#endif /* CONFIG_EARLY_PRINTK_DBGP */ - -struct usb_hcd; - -static inline int xen_dbgp_reset_prep(struct usb_hcd *hcd) -{ - return 1; /* Shouldn't this be 0? */ -} - -static inline int xen_dbgp_external_startup(struct usb_hcd *hcd) -{ - return -1; -} - -#ifdef CONFIG_EARLY_PRINTK_DBGP -/* Call backs from fotg210 host driver to fotg210 debug driver */ -extern int dbgp_external_startup(struct usb_hcd *); -extern int dbgp_reset_prep(struct usb_hcd *hcd); -#else -static inline int dbgp_reset_prep(struct usb_hcd *hcd) -{ - return xen_dbgp_reset_prep(hcd); -} -static inline int dbgp_external_startup(struct usb_hcd *hcd) -{ - return xen_dbgp_external_startup(hcd); -} -#endif - -/*-------------------------------------------------------------------------*/ - -#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma) - -/* - * EHCI Specification 0.95 Section 3.5 - * QTD: describe data transfer components (buffer, direction, ...) - * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". - * - * These are associated only with "QH" (Queue Head) structures, - * used with control, bulk, and interrupt transfers. - */ -struct fotg210_qtd { - /* first part defined by EHCI spec */ - __hc32 hw_next; /* see EHCI 3.5.1 */ - __hc32 hw_alt_next; /* see EHCI 3.5.2 */ - __hc32 hw_token; /* see EHCI 3.5.3 */ -#define QTD_TOGGLE (1 << 31) /* data toggle */ -#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) -#define QTD_IOC (1 << 15) /* interrupt on complete */ -#define QTD_CERR(tok) (((tok)>>10) & 0x3) -#define QTD_PID(tok) (((tok)>>8) & 0x3) -#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ -#define QTD_STS_HALT (1 << 6) /* halted on error */ -#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ -#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ -#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ -#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ -#define QTD_STS_STS (1 << 1) /* split transaction state */ -#define QTD_STS_PING (1 << 0) /* issue PING? */ - -#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE) -#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT) -#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS) - - __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ - __hc32 hw_buf_hi[5]; /* Appendix B */ - - /* the rest is HCD-private */ - dma_addr_t qtd_dma; /* qtd address */ - struct list_head qtd_list; /* sw qtd list */ - struct urb *urb; /* qtd's urb */ - size_t length; /* length of buffer */ -} __aligned(32); - -/* mask NakCnt+T in qh->hw_alt_next */ -#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f) - -#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) - -/*-------------------------------------------------------------------------*/ - -/* type tag from {qh,itd,fstn}->hw_next */ -#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1)) - -/* - * Now the following defines are not converted using the - * cpu_to_le32() macro anymore, since we have to support - * "dynamic" switching between be and le support, so that the driver - * can be used on one system with SoC EHCI controller using big-endian - * descriptors as well as a normal little-endian PCI EHCI controller. - */ -/* values for that type tag */ -#define Q_TYPE_ITD (0 << 1) -#define Q_TYPE_QH (1 << 1) -#define Q_TYPE_SITD (2 << 1) -#define Q_TYPE_FSTN (3 << 1) - -/* next async queue entry, or pointer to interrupt/periodic QH */ -#define QH_NEXT(fotg210, dma) \ - (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH)) - -/* for periodic/async schedules and qtd lists, mark end of list */ -#define FOTG210_LIST_END(fotg210) \ - cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */ - -/* - * Entries in periodic shadow table are pointers to one of four kinds - * of data structure. That's dictated by the hardware; a type tag is - * encoded in the low bits of the hardware's periodic schedule. Use - * Q_NEXT_TYPE to get the tag. - * - * For entries in the async schedule, the type tag always says "qh". - */ -union fotg210_shadow { - struct fotg210_qh *qh; /* Q_TYPE_QH */ - struct fotg210_itd *itd; /* Q_TYPE_ITD */ - struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ - __hc32 *hw_next; /* (all types) */ - void *ptr; -}; - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.95 Section 3.6 - * QH: describes control/bulk/interrupt endpoints - * See Fig 3-7 "Queue Head Structure Layout". - * - * These appear in both the async and (for interrupt) periodic schedules. - */ - -/* first part defined by EHCI spec */ -struct fotg210_qh_hw { - __hc32 hw_next; /* see EHCI 3.6.1 */ - __hc32 hw_info1; /* see EHCI 3.6.2 */ -#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ -#define QH_HEAD (1 << 15) /* Head of async reclamation list */ -#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ -#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ -#define QH_LOW_SPEED (1 << 12) -#define QH_FULL_SPEED (0 << 12) -#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ - __hc32 hw_info2; /* see EHCI 3.6.2 */ -#define QH_SMASK 0x000000ff -#define QH_CMASK 0x0000ff00 -#define QH_HUBADDR 0x007f0000 -#define QH_HUBPORT 0x3f800000 -#define QH_MULT 0xc0000000 - __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ - - /* qtd overlay (hardware parts of a struct fotg210_qtd) */ - __hc32 hw_qtd_next; - __hc32 hw_alt_next; - __hc32 hw_token; - __hc32 hw_buf[5]; - __hc32 hw_buf_hi[5]; -} __aligned(32); - -struct fotg210_qh { - struct fotg210_qh_hw *hw; /* Must come first */ - /* the rest is HCD-private */ - dma_addr_t qh_dma; /* address of qh */ - union fotg210_shadow qh_next; /* ptr to qh; or periodic */ - struct list_head qtd_list; /* sw qtd list */ - struct list_head intr_node; /* list of intr QHs */ - struct fotg210_qtd *dummy; - struct fotg210_qh *unlink_next; /* next on unlink list */ - - unsigned unlink_cycle; - - u8 needs_rescan; /* Dequeue during giveback */ - u8 qh_state; -#define QH_STATE_LINKED 1 /* HC sees this */ -#define QH_STATE_UNLINK 2 /* HC may still see this */ -#define QH_STATE_IDLE 3 /* HC doesn't see this */ -#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ -#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ - - u8 xacterrs; /* XactErr retry counter */ -#define QH_XACTERR_MAX 32 /* XactErr retry limit */ - - /* periodic schedule info */ - u8 usecs; /* intr bandwidth */ - u8 gap_uf; /* uframes split/csplit gap */ - u8 c_usecs; /* ... split completion bw */ - u16 tt_usecs; /* tt downstream bandwidth */ - unsigned short period; /* polling interval */ - unsigned short start; /* where polling starts */ -#define NO_FRAME ((unsigned short)~0) /* pick new start */ - - struct usb_device *dev; /* access to TT */ - unsigned is_out:1; /* bulk or intr OUT */ - unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ -}; - -/*-------------------------------------------------------------------------*/ - -/* description of one iso transaction (up to 3 KB data if highspeed) */ -struct fotg210_iso_packet { - /* These will be copied to iTD when scheduling */ - u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ - __hc32 transaction; /* itd->hw_transaction[i] |= */ - u8 cross; /* buf crosses pages */ - /* for full speed OUT splits */ - u32 buf1; -}; - -/* temporary schedule data for packets from iso urbs (both speeds) - * each packet is one logical usb transaction to the device (not TT), - * beginning at stream->next_uframe - */ -struct fotg210_iso_sched { - struct list_head td_list; - unsigned span; - struct fotg210_iso_packet packet[0]; -}; - -/* - * fotg210_iso_stream - groups all (s)itds for this endpoint. - * acts like a qh would, if EHCI had them for ISO. - */ -struct fotg210_iso_stream { - /* first field matches fotg210_hq, but is NULL */ - struct fotg210_qh_hw *hw; - - u8 bEndpointAddress; - u8 highspeed; - struct list_head td_list; /* queued itds */ - struct list_head free_list; /* list of unused itds */ - struct usb_device *udev; - struct usb_host_endpoint *ep; - - /* output of (re)scheduling */ - int next_uframe; - __hc32 splits; - - /* the rest is derived from the endpoint descriptor, - * trusting urb->interval == f(epdesc->bInterval) and - * including the extra info for hw_bufp[0..2] - */ - u8 usecs, c_usecs; - u16 interval; - u16 tt_usecs; - u16 maxp; - u16 raw_mask; - unsigned bandwidth; - - /* This is used to initialize iTD's hw_bufp fields */ - __hc32 buf0; - __hc32 buf1; - __hc32 buf2; - - /* this is used to initialize sITD's tt info */ - __hc32 address; -}; - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.95 Section 3.3 - * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" - * - * Schedule records for high speed iso xfers - */ -struct fotg210_itd { - /* first part defined by EHCI spec */ - __hc32 hw_next; /* see EHCI 3.3.1 */ - __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ -#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ -#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */ -#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */ -#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ -#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) -#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */ - -#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE) - - __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ - __hc32 hw_bufp_hi[7]; /* Appendix B */ - - /* the rest is HCD-private */ - dma_addr_t itd_dma; /* for this itd */ - union fotg210_shadow itd_next; /* ptr to periodic q entry */ - - struct urb *urb; - struct fotg210_iso_stream *stream; /* endpoint's queue */ - struct list_head itd_list; /* list of stream's itds */ - - /* any/all hw_transactions here may be used by that urb */ - unsigned frame; /* where scheduled */ - unsigned pg; - unsigned index[8]; /* in urb->iso_frame_desc */ -} __aligned(32); - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.96 Section 3.7 - * Periodic Frame Span Traversal Node (FSTN) - * - * Manages split interrupt transactions (using TT) that span frame boundaries - * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN - * makes the HC jump (back) to a QH to scan for fs/ls QH completions until - * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. - */ -struct fotg210_fstn { - __hc32 hw_next; /* any periodic q entry */ - __hc32 hw_prev; /* qh or FOTG210_LIST_END */ - - /* the rest is HCD-private */ - dma_addr_t fstn_dma; - union fotg210_shadow fstn_next; /* ptr to periodic q entry */ -} __aligned(32); - -/*-------------------------------------------------------------------------*/ - -/* Prepare the PORTSC wakeup flags during controller suspend/resume */ - -#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ - fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup); - -#define fotg210_prepare_ports_for_controller_resume(fotg210) \ - fotg210_adjust_port_wakeup_flags(fotg210, false, false); - -/*-------------------------------------------------------------------------*/ - -/* - * Some EHCI controllers have a Transaction Translator built into the - * root hub. This is a non-standard feature. Each controller will need - * to add code to the following inline functions, and call them as - * needed (mostly in root hub code). - */ - -static inline unsigned int -fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc) -{ - return (readl(&fotg210->regs->otgcsr) - & OTGCSR_HOST_SPD_TYP) >> 22; -} - -/* Returns the speed of a device attached to a port on the root hub. */ -static inline unsigned int -fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc) -{ - switch (fotg210_get_speed(fotg210, portsc)) { - case 0: - return 0; - case 1: - return USB_PORT_STAT_LOW_SPEED; - case 2: - default: - return USB_PORT_STAT_HIGH_SPEED; - } -} - -/*-------------------------------------------------------------------------*/ - -#define fotg210_has_fsl_portno_bug(e) (0) - -/* - * While most USB host controllers implement their registers in - * little-endian format, a minority (celleb companion chip) implement - * them in big endian format. - * - * This attempts to support either format at compile time without a - * runtime penalty, or both formats with the additional overhead - * of checking a flag bit. - * - */ - -#define fotg210_big_endian_mmio(e) 0 -#define fotg210_big_endian_capbase(e) 0 - -static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210, - __u32 __iomem *regs) -{ - return readl(regs); -} - -static inline void fotg210_writel(const struct fotg210_hcd *fotg210, - const unsigned int val, __u32 __iomem *regs) -{ - writel(val, regs); -} - -/* cpu to fotg210 */ -static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x) -{ - return cpu_to_le32(x); -} - -/* fotg210 to cpu */ -static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x) -{ - return le32_to_cpu(x); -} - -static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210, - const __hc32 *x) -{ - return le32_to_cpup(x); -} - -/*-------------------------------------------------------------------------*/ - -static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) -{ - return fotg210_readl(fotg210, &fotg210->regs->frame_index); -} - -#define fotg210_itdlen(urb, desc, t) ({ \ - usb_pipein((urb)->pipe) ? \ - (desc)->length - FOTG210_ITD_LENGTH(t) : \ - FOTG210_ITD_LENGTH(t); \ -}) -/*-------------------------------------------------------------------------*/ - -#ifndef DEBUG -#define STUB_DEBUG_FILES -#endif /* DEBUG */ - -/*-------------------------------------------------------------------------*/ - -#endif /* __LINUX_FOTG210_H */ -- cgit v1.2.3-58-ga151 From d102e78f7035f2d6896ced598122b878afd1c352 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 1 Jul 2013 11:23:24 +0300 Subject: usb: class: cdc-acm: be careful with bInterval bInterval must be on the range 1 - 16, if we want to pass the maximum allowed, we should be passing 16 Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 9f49bfe4c6f4..3e7560f004f8 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1295,7 +1295,7 @@ skip_countries: usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, /* works around buggy devices */ - epctrl->bInterval ? epctrl->bInterval : 0xff); + epctrl->bInterval ? epctrl->bInterval : 16); acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; acm->ctrlurb->transfer_dma = acm->ctrl_dma; -- cgit v1.2.3-58-ga151 From afb8aae89890e65bd4b828de38bd430d4f31caa8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 1 Jul 2013 11:23:23 +0300 Subject: usb: atm: speedtch: be careful with bInterval bInterval must be on the range 1 - 16, if we want to pass the maximum allowed, we should be passing 16. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/speedtch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 807627b36cc8..69461d653972 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -888,7 +888,7 @@ static int speedtch_bind(struct usbatm_data *usbatm, usb_fill_int_urb(instance->int_urb, usb_dev, usb_rcvintpipe(usb_dev, ENDPOINT_INT), instance->int_data, sizeof(instance->int_data), - speedtch_handle_int, instance, 50); + speedtch_handle_int, instance, 16); else usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__); -- cgit v1.2.3-58-ga151 From 1353aa53851e3bc7f2c6365753d9c2783c34f882 Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Tue, 2 Jul 2013 20:13:51 -0700 Subject: usb: misc: EHSET Test Fixture device driver for host compliance An Embedded Host High-Speed Electrical Test (EHSET) test fixture is used to initiate test modes on a host controller in order to perform the high speed electrical testing procedure for USB-IF compliance. When this test fixture is connected to a host, it can enumerate as one of several selectable VID/PID pairs, each corresponding to one of the following test modes: * TEST_SE0_NAK * TEST_J * TEST_K * TEST_PACKET * HS_HOST_PORT_SUSPEND_RESUME * SINGLE_STEP_GET_DEV_DESC * SINGLE_STEP_SET_FEATURE The USB EHSET procedure can be found here: http://www.usb.org/developers/onthego/EHSET_v1.01.pdf Signed-off-by: Manu Gautam [jackp@codeaurora.org: imported from commit 073c9409 on codeaurora.org; minor cleanup and updated author email] Signed-off-by: Jack Pham Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/Kconfig | 13 +++++ drivers/usb/misc/Makefile | 1 + drivers/usb/misc/ehset.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 drivers/usb/misc/ehset.c (limited to 'drivers/usb') diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index a51e7d6afda9..ca91420f5c21 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -200,6 +200,19 @@ config USB_TEST See for more information, including sample test device firmware and "how to use it". +config USB_EHSET_TEST_FIXTURE + tristate "USB EHSET Test Fixture driver" + help + Say Y here if you want to support the special test fixture device + used for the USB-IF Embedded Host High-Speed Electrical Test procedure. + + When the test fixture is connected, it can enumerate as one of several + VID/PID pairs. This driver then initiates a corresponding test mode on + the downstream port to which the test fixture is attached. + + See for more + information. + config USB_ISIGHTFW tristate "iSight firmware loading support" select FW_LOADER diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 5a896f319118..e748fd5dbe94 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_USB_LED) += usbled.o obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_TEST) += usbtest.o +obj-$(CONFIG_USB_EHSET_TEST_FIXTURE) += ehset.o obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o obj-$(CONFIG_USB_USS720) += uss720.o obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o diff --git a/drivers/usb/misc/ehset.c b/drivers/usb/misc/ehset.c new file mode 100644 index 000000000000..d2864b3f41fe --- /dev/null +++ b/drivers/usb/misc/ehset.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define TEST_SE0_NAK_PID 0x0101 +#define TEST_J_PID 0x0102 +#define TEST_K_PID 0x0103 +#define TEST_PACKET_PID 0x0104 +#define TEST_HS_HOST_PORT_SUSPEND_RESUME 0x0106 +#define TEST_SINGLE_STEP_GET_DEV_DESC 0x0107 +#define TEST_SINGLE_STEP_SET_FEATURE 0x0108 + +static int ehset_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret = -EINVAL; + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_device *hub_udev = dev->parent; + struct usb_device_descriptor *buf; + u8 portnum = dev->portnum; + u16 test_pid = le16_to_cpu(dev->descriptor.idProduct); + + switch (test_pid) { + case TEST_SE0_NAK_PID: + ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, + USB_PORT_FEAT_TEST, + (TEST_SE0_NAK << 8) | portnum, + NULL, 0, 1000); + break; + case TEST_J_PID: + ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, + USB_PORT_FEAT_TEST, + (TEST_J << 8) | portnum, + NULL, 0, 1000); + break; + case TEST_K_PID: + ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, + USB_PORT_FEAT_TEST, + (TEST_K << 8) | portnum, + NULL, 0, 1000); + break; + case TEST_PACKET_PID: + ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, + USB_PORT_FEAT_TEST, + (TEST_PACKET << 8) | portnum, + NULL, 0, 1000); + break; + case TEST_HS_HOST_PORT_SUSPEND_RESUME: + /* Test: wait for 15secs -> suspend -> 15secs delay -> resume */ + msleep(15 * 1000); + ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, + USB_PORT_FEAT_SUSPEND, portnum, + NULL, 0, 1000); + if (ret < 0) + break; + + msleep(15 * 1000); + ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, + USB_PORT_FEAT_SUSPEND, portnum, + NULL, 0, 1000); + break; + case TEST_SINGLE_STEP_GET_DEV_DESC: + /* Test: wait for 15secs -> GetDescriptor request */ + msleep(15 * 1000); + buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + USB_DT_DEVICE << 8, 0, + buf, USB_DT_DEVICE_SIZE, + USB_CTRL_GET_TIMEOUT); + kfree(buf); + break; + case TEST_SINGLE_STEP_SET_FEATURE: + /* unsupported for now */ + default: + dev_err(&intf->dev, "%s: unsupported PID: 0x%x\n", + __func__, test_pid); + } + + return (ret < 0) ? ret : 0; +} + +static void ehset_disconnect(struct usb_interface *intf) +{ +} + +static const struct usb_device_id ehset_id_table[] = { + { USB_DEVICE(0x1a0a, TEST_SE0_NAK_PID) }, + { USB_DEVICE(0x1a0a, TEST_J_PID) }, + { USB_DEVICE(0x1a0a, TEST_K_PID) }, + { USB_DEVICE(0x1a0a, TEST_PACKET_PID) }, + { USB_DEVICE(0x1a0a, TEST_HS_HOST_PORT_SUSPEND_RESUME) }, + { USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_GET_DEV_DESC) }, + { USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_SET_FEATURE) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, ehset_id_table); + +static struct usb_driver ehset_driver = { + .name = "usb_ehset_test", + .probe = ehset_probe, + .disconnect = ehset_disconnect, + .id_table = ehset_id_table, +}; + +module_usb_driver(ehset_driver); + +MODULE_DESCRIPTION("USB Driver for EHSET Test Fixture"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From c4b51a431561f0bacc1740aa27f080bf95c4d6c7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 11 Jul 2013 14:58:23 -0400 Subject: USB: remove redundant "#if" This patch removes a redundant nested "#ifdef CONFIG_PM" from the hub driver. It also adds a label to the "#endif" line corresponding to the outer "#ifdef CONFIG_PM". Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4191db32f12c..f735f6bc929c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2829,7 +2829,6 @@ void usb_enable_ltm(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_enable_ltm); -#ifdef CONFIG_PM /* * usb_disable_function_remotewakeup - disable usb3.0 * device's function remote wakeup @@ -3249,8 +3248,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) return status; } -#endif /* CONFIG_PM */ - #ifdef CONFIG_PM_RUNTIME /* caller has locked udev */ @@ -3811,7 +3808,8 @@ EXPORT_SYMBOL_GPL(usb_disable_ltm); void usb_enable_ltm(struct usb_device *udev) { } EXPORT_SYMBOL_GPL(usb_enable_ltm); -#endif + +#endif /* CONFIG_PM */ /* USB 2.0 spec, 7.1.7.3 / fig 7-29: -- cgit v1.2.3-58-ga151 From aaf6b52d50f85ed792c9c8987f5169f3dce2adea Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 22 Jul 2013 15:04:50 +0300 Subject: USB: host: Use usb_hcd_platform_shutdown() wherever possible Most HCD drivers are doing the same thing in their ".shutdown" callback so it makes sense to use the generic usb_hcd_platform_shutdown() handler there. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-grlib.c | 11 +---------- drivers/usb/host/ehci-mxc.c | 10 +--------- drivers/usb/host/ehci-omap.c | 10 +--------- drivers/usb/host/ehci-ppc-of.c | 11 +---------- drivers/usb/host/ehci-s5p.c | 10 +--------- drivers/usb/host/ehci-xilinx-of.c | 17 +---------------- drivers/usb/host/ohci-omap3.c | 10 +--------- drivers/usb/host/ohci-ppc-of.c | 11 +---------- 8 files changed, 8 insertions(+), 82 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index a77bd8dc33f4..83ab51af250f 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -167,15 +167,6 @@ static int ehci_hcd_grlib_remove(struct platform_device *op) } -static void ehci_hcd_grlib_shutdown(struct platform_device *op) -{ - struct usb_hcd *hcd = platform_get_drvdata(op); - - if (hcd->driver->shutdown) - hcd->driver->shutdown(hcd); -} - - static const struct of_device_id ehci_hcd_grlib_of_match[] = { { .name = "GAISLER_EHCI", @@ -191,7 +182,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match); static struct platform_driver ehci_grlib_driver = { .probe = ehci_hcd_grlib_probe, .remove = ehci_hcd_grlib_remove, - .shutdown = ehci_hcd_grlib_shutdown, + .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "grlib-ehci", .owner = THIS_MODULE, diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index e4c34ac386c0..030b53958845 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -197,20 +197,12 @@ static int ehci_mxc_drv_remove(struct platform_device *pdev) return 0; } -static void ehci_mxc_drv_shutdown(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - - if (hcd->driver->shutdown) - hcd->driver->shutdown(hcd); -} - MODULE_ALIAS("platform:mxc-ehci"); static struct platform_driver ehci_mxc_driver = { .probe = ehci_mxc_drv_probe, .remove = ehci_mxc_drv_remove, - .shutdown = ehci_mxc_drv_shutdown, + .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "mxc-ehci", }, diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 9bd7dfe3315b..4384d07115d1 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -278,14 +278,6 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) return 0; } -static void ehci_hcd_omap_shutdown(struct platform_device *pdev) -{ - struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); - - if (hcd->driver->shutdown) - hcd->driver->shutdown(hcd); -} - static const struct of_device_id omap_ehci_dt_ids[] = { { .compatible = "ti,ehci-omap" }, { } @@ -296,7 +288,7 @@ MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids); static struct platform_driver ehci_hcd_omap_driver = { .probe = ehci_hcd_omap_probe, .remove = ehci_hcd_omap_remove, - .shutdown = ehci_hcd_omap_shutdown, + .shutdown = usb_hcd_platform_shutdown, /*.suspend = ehci_hcd_omap_suspend, */ /*.resume = ehci_hcd_omap_resume, */ .driver = { diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 86da09c0f8d0..932293fa32de 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -215,15 +215,6 @@ static int ehci_hcd_ppc_of_remove(struct platform_device *op) } -static void ehci_hcd_ppc_of_shutdown(struct platform_device *op) -{ - struct usb_hcd *hcd = platform_get_drvdata(op); - - if (hcd->driver->shutdown) - hcd->driver->shutdown(hcd); -} - - static const struct of_device_id ehci_hcd_ppc_of_match[] = { { .compatible = "usb-ehci", @@ -236,7 +227,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match); static struct platform_driver ehci_hcd_ppc_of_driver = { .probe = ehci_hcd_ppc_of_probe, .remove = ehci_hcd_ppc_of_remove, - .shutdown = ehci_hcd_ppc_of_shutdown, + .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ehci", .owner = THIS_MODULE, diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 7cc26e621aa7..aba645bd4866 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -220,14 +220,6 @@ static int s5p_ehci_remove(struct platform_device *pdev) return 0; } -static void s5p_ehci_shutdown(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - - if (hcd->driver->shutdown) - hcd->driver->shutdown(hcd); -} - #ifdef CONFIG_PM static int s5p_ehci_suspend(struct device *dev) { @@ -297,7 +289,7 @@ MODULE_DEVICE_TABLE(of, exynos_ehci_match); static struct platform_driver s5p_ehci_driver = { .probe = s5p_ehci_probe, .remove = s5p_ehci_remove, - .shutdown = s5p_ehci_shutdown, + .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "s5p-ehci", .owner = THIS_MODULE, diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index 35c7f90384a6..eba962e6ebfb 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -220,21 +220,6 @@ static int ehci_hcd_xilinx_of_remove(struct platform_device *op) return 0; } -/** - * ehci_hcd_xilinx_of_shutdown - shutdown the hcd - * @op: pointer to platform_device structure that is to be removed - * - * Properly shutdown the hcd, call driver's shutdown routine. - */ -static void ehci_hcd_xilinx_of_shutdown(struct platform_device *op) -{ - struct usb_hcd *hcd = platform_get_drvdata(op); - - if (hcd->driver->shutdown) - hcd->driver->shutdown(hcd); -} - - static const struct of_device_id ehci_hcd_xilinx_of_match[] = { {.compatible = "xlnx,xps-usb-host-1.00.a",}, {}, @@ -244,7 +229,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match); static struct platform_driver ehci_hcd_xilinx_of_driver = { .probe = ehci_hcd_xilinx_of_probe, .remove = ehci_hcd_xilinx_of_remove, - .shutdown = ehci_hcd_xilinx_of_shutdown, + .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xilinx-of-ehci", .owner = THIS_MODULE, diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 8f713571a0b7..a09af26f69ed 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -231,14 +231,6 @@ static int ohci_hcd_omap3_remove(struct platform_device *pdev) return 0; } -static void ohci_hcd_omap3_shutdown(struct platform_device *pdev) -{ - struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); - - if (hcd->driver->shutdown) - hcd->driver->shutdown(hcd); -} - static const struct of_device_id omap_ohci_dt_ids[] = { { .compatible = "ti,ohci-omap3" }, { } @@ -249,7 +241,7 @@ MODULE_DEVICE_TABLE(of, omap_ohci_dt_ids); static struct platform_driver ohci_hcd_omap3_driver = { .probe = ohci_hcd_omap3_probe, .remove = ohci_hcd_omap3_remove, - .shutdown = ohci_hcd_omap3_shutdown, + .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ohci-omap3", .of_match_table = omap_ohci_dt_ids, diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 8294e2fcc2f6..75f5a1e2f01e 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -200,15 +200,6 @@ static int ohci_hcd_ppc_of_remove(struct platform_device *op) return 0; } -static void ohci_hcd_ppc_of_shutdown(struct platform_device *op) -{ - struct usb_hcd *hcd = platform_get_drvdata(op); - - if (hcd->driver->shutdown) - hcd->driver->shutdown(hcd); -} - - static const struct of_device_id ohci_hcd_ppc_of_match[] = { #ifdef CONFIG_USB_OHCI_HCD_PPC_OF_BE { @@ -243,7 +234,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_ppc_of_match); static struct platform_driver ohci_hcd_ppc_of_driver = { .probe = ohci_hcd_ppc_of_probe, .remove = ohci_hcd_ppc_of_remove, - .shutdown = ohci_hcd_ppc_of_shutdown, + .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ohci", .owner = THIS_MODULE, -- cgit v1.2.3-58-ga151 From 5dc50c357d5ce8f78e148d20a38e4e66b12d550f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 12 Jul 2013 10:04:18 +0200 Subject: usbfs: Allow printer class 'get_device_id' without needing to claim the intf For certain (HP) printers the printer device_id does not only contain a static part identifying the printer, but it also contains a dynamic part giving printer status, ink level, etc. To get to this info various userspace utilities need to be able to make a printer class 'get_device_id' request without first claiming the interface (as that is in use for the actual printer driver). Since the printer class 'get_device_id' request does not change interface settings in anyway, allowing this without claiming the interface should not cause any issues. CC: Sanjay Kumar CC: Alan Stern Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 05986507b585..737e3c19967b 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -725,15 +725,15 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, /* * check for the special corner case 'get_device_id' in the printer - * class specification, where wIndex is (interface << 8 | altsetting) - * instead of just interface + * class specification, which we always want to allow as it is used + * to query things like ink level, etc. */ if (requesttype == 0xa1 && request == 0) { alt_setting = usb_find_alt_setting(ps->dev->actconfig, index >> 8, index & 0xff); if (alt_setting && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER) - index >>= 8; + return 0; } index &= 0xff; -- cgit v1.2.3-58-ga151 From 92f78ddb10eab664c8934f0f3a9f284167ac45f9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jul 2013 11:59:40 +0300 Subject: usbtmc: remove trailing spaces Recent patch series introduces few trailing spaces. This patch removes them. No functional change. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 609dbc2f7151..92eaf9d6de8b 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -554,14 +554,14 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, if (remaining > n_characters) remaining = n_characters; /* Remove padding if it exists */ - if (actual > remaining) + if (actual > remaining) actual = remaining; } else { if (this_part > n_characters) this_part = n_characters; /* Remove padding if it exists */ - if (actual > this_part) + if (actual > this_part) actual = this_part; } @@ -585,7 +585,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, done += actual; } else { - if (actual > remaining) + if (actual > remaining) actual = remaining; remaining -= actual; -- cgit v1.2.3-58-ga151 From f4d844cb6c073998db0c7fe3d76c6c070333054e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jul 2013 11:59:42 +0300 Subject: usbtmc: call pr_err instead of plain printk Additionally remove useless label. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 92eaf9d6de8b..21114ab99d96 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -19,6 +19,8 @@ * http://www.gnu.org/copyleft/gpl.html. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -130,10 +132,8 @@ static int usbtmc_open(struct inode *inode, struct file *filp) intf = usb_find_interface(&usbtmc_driver, iminor(inode)); if (!intf) { - printk(KERN_ERR KBUILD_MODNAME - ": can not find device for minor %d", iminor(inode)); - retval = -ENODEV; - goto exit; + pr_err("can not find device for minor %d", iminor(inode)); + return -ENODEV; } data = usb_get_intfdata(intf); @@ -142,7 +142,6 @@ static int usbtmc_open(struct inode *inode, struct file *filp) /* Store pointer in file structure's private data field */ filp->private_data = data; -exit: return retval; } -- cgit v1.2.3-58-ga151 From bbf4976e7715da4a3e3599a500eb20d6be8b5046 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jul 2013 11:59:43 +0300 Subject: usbtmc: remove redundant braces There is a few cases where braces are not needed. This patch removes unnecessary '& 255' pieces as well when lvalue type is u8. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 21114ab99d96..d683f90a505b 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -393,12 +393,12 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t */ buffer[0] = 2; buffer[1] = data->bTag; - buffer[2] = ~(data->bTag); + buffer[2] = ~data->bTag; buffer[3] = 0; /* Reserved */ - buffer[4] = (transfer_size) & 255; - buffer[5] = ((transfer_size) >> 8) & 255; - buffer[6] = ((transfer_size) >> 16) & 255; - buffer[7] = ((transfer_size) >> 24) & 255; + buffer[4] = transfer_size >> 0; + buffer[5] = transfer_size >> 8; + buffer[6] = transfer_size >> 16; + buffer[7] = transfer_size >> 24; buffer[8] = data->TermCharEnabled * 2; /* Use term character? */ buffer[9] = data->TermChar; @@ -417,7 +417,7 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t /* Increment bTag -- and increment again if zero */ data->bTag++; if (!data->bTag) - (data->bTag)++; + data->bTag++; if (retval < 0) { dev_err(&data->intf->dev, "usb_bulk_msg in send_request_dev_dep_msg_in() returned %d\n", retval); @@ -472,7 +472,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, done = 0; while (remaining > 0) { - if (!(data->rigol_quirk)) { + if (!data->rigol_quirk) { dev_dbg(dev, "usb_bulk_msg_in: remaining(%zu), count(%zu)\n", remaining, count); if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE - 3) @@ -509,7 +509,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, } /* Parse header in first packet */ - if ((done == 0) || (!(data->rigol_quirk))) { + if ((done == 0) || !data->rigol_quirk) { /* Sanity checks for the header */ if (actual < USBTMC_HEADER_SIZE) { dev_err(dev, "Device sent too small first packet: %u < %u\n", actual, USBTMC_HEADER_SIZE); @@ -569,7 +569,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, remaining -= actual; /* Terminate if end-of-message bit received from device */ - if ((buffer[8] & 0x01) && (actual >= n_characters)) + if ((buffer[8] & 0x01) && (actual >= n_characters)) remaining = 0; dev_dbg(dev, "Bulk-IN header: remaining(%zu), buf(%p), buffer(%p) done(%zu)\n", remaining,buf,buffer,done); @@ -650,12 +650,12 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, /* Setup IO buffer for DEV_DEP_MSG_OUT message */ buffer[0] = 1; buffer[1] = data->bTag; - buffer[2] = ~(data->bTag); + buffer[2] = ~data->bTag; buffer[3] = 0; /* Reserved */ - buffer[4] = this_part & 255; - buffer[5] = (this_part >> 8) & 255; - buffer[6] = (this_part >> 16) & 255; - buffer[7] = (this_part >> 24) & 255; + buffer[4] = this_part >> 0; + buffer[5] = this_part >> 8; + buffer[6] = this_part >> 16; + buffer[7] = this_part >> 24; /* buffer[8] is set above... */ buffer[9] = 0; /* Reserved */ buffer[10] = 0; /* Reserved */ -- cgit v1.2.3-58-ga151 From e6c7efdcb76f11b04e3d3f71c8d764ab75c9423b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jul 2013 11:59:41 +0300 Subject: usbtmc: convert to devm_kzalloc kfree(data) will be called implicitly. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index d683f90a505b..60dd8918aeb9 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -121,7 +121,6 @@ static void usbtmc_delete(struct kref *kref) struct usbtmc_device_data *data = to_usbtmc_data(kref); usb_put_dev(data->usb_dev); - kfree(data); } static int usbtmc_open(struct inode *inode, struct file *filp) @@ -1101,7 +1100,7 @@ static int usbtmc_probe(struct usb_interface *intf, dev_dbg(&intf->dev, "%s called\n", __func__); - data = kmalloc(sizeof(struct usbtmc_device_data), GFP_KERNEL); + data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); if (!data) { dev_err(&intf->dev, "Unable to allocate kernel memory\n"); return -ENOMEM; -- cgit v1.2.3-58-ga151 From 140983c283b302167b387646218a97f112cc0c03 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 19 Jul 2013 10:15:18 +0800 Subject: USB: usb-skeleton.c: add retry for nonblocking read Updated skel_read() in usb-skeleton.c. When there is no data in the buffer, we would allow retry for both blocking and nonblocking cases. Original logic give retry only for blocking case. Actually we can also allow retry for nonblocking case. This will reuse the existing retry logic and handle the return of -EAGAIN in one place. Also if the data to be read is short and can be retrieved in quick time, we can also give a chance for nonblocking case and may catch the data and copy it back to userspace in one read() call too. Signed-off-by: Chen Wang Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usb-skeleton.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 7ed3b039dbe8..ff97652343a3 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -325,9 +325,8 @@ retry: rv = skel_do_read_io(dev, count); if (rv < 0) goto exit; - else if (!(file->f_flags & O_NONBLOCK)) + else goto retry; - rv = -EAGAIN; } exit: mutex_unlock(&dev->io_mutex); -- cgit v1.2.3-58-ga151 From 8bd3902d8be2b4ba484d5dbbe0f30b73b2f8c017 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 1 Jul 2013 15:52:42 -0700 Subject: usb: ohci-ep93xx: use devm_ioremap_resource() Use devm_ioremap_resource() to make the code a bit cleaner and simpler. Signed-off-by: H Hartley Sweeten Acked-by: Alan Stern Cc: Lennert Buytenhek Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-ep93xx.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 8704e9fa5a80..4e12f67a32dd 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -43,38 +43,37 @@ static void ep93xx_stop_hc(struct device *dev) static int usb_hcd_ep93xx_probe(const struct hc_driver *driver, struct platform_device *pdev) { - int retval; struct usb_hcd *hcd; + struct resource *res; + int retval; if (pdev->resource[1].flags != IORESOURCE_IRQ) { dev_dbg(&pdev->dev, "resource[1] is not IORESOURCE_IRQ\n"); return -ENOMEM; } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + hcd = usb_create_hcd(driver, &pdev->dev, "ep93xx"); if (hcd == NULL) return -ENOMEM; - hcd->rsrc_start = pdev->resource[0].start; - hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - usb_put_hcd(hcd); - retval = -EBUSY; - goto err1; - } + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (hcd->regs == NULL) { - dev_dbg(&pdev->dev, "ioremap failed\n"); - retval = -ENOMEM; - goto err2; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); + goto err_put_hcd; } usb_host_clock = clk_get(&pdev->dev, NULL); if (IS_ERR(usb_host_clock)) { dev_dbg(&pdev->dev, "clk_get failed\n"); retval = PTR_ERR(usb_host_clock); - goto err3; + goto err_put_hcd; } ep93xx_start_hc(&pdev->dev); @@ -86,11 +85,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver, return retval; ep93xx_stop_hc(&pdev->dev); -err3: - iounmap(hcd->regs); -err2: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -err1: +err_put_hcd: usb_put_hcd(hcd); return retval; @@ -102,8 +97,6 @@ static void usb_hcd_ep93xx_remove(struct usb_hcd *hcd, usb_remove_hcd(hcd); ep93xx_stop_hc(&pdev->dev); clk_put(usb_host_clock); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); } -- cgit v1.2.3-58-ga151 From 8fb35f2d3189f27fe51fb485e9b9751b202d585b Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 1 Jul 2013 15:52:51 -0700 Subject: usb: ohci-ep93xx: use platform_get_irq() Use platform_get_irq() instead of accessing the platform_device resources directly. Signed-off-by: H Hartley Sweeten Acked-by: Alan Stern Cc: Lennert Buytenhek Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-ep93xx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 4e12f67a32dd..b4f5e6403991 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -45,12 +45,12 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver, { struct usb_hcd *hcd; struct resource *res; + int irq; int retval; - if (pdev->resource[1].flags != IORESOURCE_IRQ) { - dev_dbg(&pdev->dev, "resource[1] is not IORESOURCE_IRQ\n"); - return -ENOMEM; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -80,7 +80,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, pdev->resource[1].start, 0); + retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) return retval; -- cgit v1.2.3-58-ga151 From 09ae8e7ead08e403413ceb4ba0f47eb324d03995 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 1 Jul 2013 15:52:59 -0700 Subject: usb: ohci-ep93xx: use devm_clk_get() Use devm_clk_get() to make the code a bit cleaner and simpler. This also fixes a bug where a clk_put() is not done if usb_add_hcd() fails. Signed-off-by: H Hartley Sweeten Acked-by: Alan Stern Cc: Lennert Buytenhek Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-ep93xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index b4f5e6403991..28fa6b8b5acd 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -69,9 +69,8 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver, goto err_put_hcd; } - usb_host_clock = clk_get(&pdev->dev, NULL); + usb_host_clock = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(usb_host_clock)) { - dev_dbg(&pdev->dev, "clk_get failed\n"); retval = PTR_ERR(usb_host_clock); goto err_put_hcd; } @@ -96,7 +95,6 @@ static void usb_hcd_ep93xx_remove(struct usb_hcd *hcd, { usb_remove_hcd(hcd); ep93xx_stop_hc(&pdev->dev); - clk_put(usb_host_clock); usb_put_hcd(hcd); } -- cgit v1.2.3-58-ga151 From af3f233fd27b6a59cd6ea46a7dc048fff3ef2740 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 1 Jul 2013 15:53:02 -0700 Subject: usb: ohci-ep93xx: tidy up driver (*probe) and (*remove) Merge the usb_hcd_ep93xx_probe() into ohci_hcd_ep93xx_drv_probe() and the usb_hcd_ep93xx_remove() into ohci_hcd_ep93xx_drv_remove(). As Alan Stern pointed out, there is no reason for them to be separate. Also, as Alan Stern suggested, eliminate the ep93xx_start_hc() and ep93xx_stop_hc() routines and simply call clk_enable() and clk_disable() directly. The extra level of redirection does not add any clarity. Signed-off-by: H Hartley Sweeten Acked-by: Alan Stern Cc: Lennert Buytenhek Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-ep93xx.c | 128 +++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 76 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 28fa6b8b5acd..84a20d5223b9 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -30,74 +30,6 @@ static struct clk *usb_host_clock; -static void ep93xx_start_hc(struct device *dev) -{ - clk_enable(usb_host_clock); -} - -static void ep93xx_stop_hc(struct device *dev) -{ - clk_disable(usb_host_clock); -} - -static int usb_hcd_ep93xx_probe(const struct hc_driver *driver, - struct platform_device *pdev) -{ - struct usb_hcd *hcd; - struct resource *res; - int irq; - int retval; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - - hcd = usb_create_hcd(driver, &pdev->dev, "ep93xx"); - if (hcd == NULL) - return -ENOMEM; - - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - - hcd->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(hcd->regs)) { - retval = PTR_ERR(hcd->regs); - goto err_put_hcd; - } - - usb_host_clock = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(usb_host_clock)) { - retval = PTR_ERR(usb_host_clock); - goto err_put_hcd; - } - - ep93xx_start_hc(&pdev->dev); - - ohci_hcd_init(hcd_to_ohci(hcd)); - - retval = usb_add_hcd(hcd, irq, 0); - if (retval == 0) - return retval; - - ep93xx_stop_hc(&pdev->dev); -err_put_hcd: - usb_put_hcd(hcd); - - return retval; -} - -static void usb_hcd_ep93xx_remove(struct usb_hcd *hcd, - struct platform_device *pdev) -{ - usb_remove_hcd(hcd); - ep93xx_stop_hc(&pdev->dev); - usb_put_hcd(hcd); -} - static int ohci_ep93xx_start(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci(hcd); @@ -138,15 +70,57 @@ static struct hc_driver ohci_ep93xx_hc_driver = { .start_port_reset = ohci_start_port_reset, }; -extern int usb_disabled(void); - static int ohci_hcd_ep93xx_drv_probe(struct platform_device *pdev) { + struct usb_hcd *hcd; + struct resource *res; + int irq; int ret; - ret = -ENODEV; - if (!usb_disabled()) - ret = usb_hcd_ep93xx_probe(&ohci_ep93xx_hc_driver, pdev); + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + hcd = usb_create_hcd(&ohci_ep93xx_hc_driver, &pdev->dev, "ep93xx"); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); + goto err_put_hcd; + } + + usb_host_clock = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(usb_host_clock)) { + ret = PTR_ERR(usb_host_clock); + goto err_put_hcd; + } + + clk_enable(usb_host_clock); + + ohci_hcd_init(hcd_to_ohci(hcd)); + + ret = usb_add_hcd(hcd, irq, 0); + if (ret) + goto err_clk_disable; + + return 0; + +err_clk_disable: + clk_disable(usb_host_clock); +err_put_hcd: + usb_put_hcd(hcd); return ret; } @@ -155,7 +129,9 @@ static int ohci_hcd_ep93xx_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - usb_hcd_ep93xx_remove(hcd, pdev); + usb_remove_hcd(hcd); + clk_disable(usb_host_clock); + usb_put_hcd(hcd); return 0; } @@ -170,7 +146,7 @@ static int ohci_hcd_ep93xx_drv_suspend(struct platform_device *pdev, pm_message_ msleep(5); ohci->next_statechange = jiffies; - ep93xx_stop_hc(&pdev->dev); + clk_disable(usb_host_clock); return 0; } @@ -183,7 +159,7 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev) msleep(5); ohci->next_statechange = jiffies; - ep93xx_start_hc(&pdev->dev); + clk_enable(usb_host_clock); ohci_resume(hcd, false); return 0; -- cgit v1.2.3-58-ga151 From 4e682bbd32708f3eab55b66960933b36bc00e31a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 4 Jul 2013 01:39:10 -0400 Subject: usb: limit OMAP related USB options to OMAP2PLUS platforms commit 57f6ce072e35770a63be0c5d5e82f90d8da7d665 ("usb: phy: add a new driver for usb3 phy") added the new Kconfig option OMAP_USB3, but it had no dependencies whatsoever, and hence became available across all arch/platforms. Which presumably caused this to show up in x86 randconfig: warning: (USB_MUSB_HDRC && OMAP_USB3) selects \ OMAP_CONTROL_USB which has unmet direct \ dependencies (USB_SUPPORT && ARCH_OMAP2PLUS) Then commit 6992819feb39cb9adac72170555d957d07f869f2 ("usb: phy: fix Kconfig warning") was added. However, this just deleted the ARCH_OMAP2PLUS dependency from OMAP_CONTROL_USB, further compounding the problem by opening up OMAP_CONTROL_USB to all arch/platforms as well. Earlier it was suggested[1] that we revert the change of 6992819feb to restore the dependency, and add a same ARCH_OMAP2PLUS dependency to the new OMAP_USB3 entry. However that was discouraged on the grounds of people wanting the extra sanity compile testing on x86, even though the driver could probably never be used there. Now we have CONFIG_COMPILE_TEST, so developers who value the ability to compile drivers on an architecture that it never can be used for can have that, and people who want dependencies to shield them from seeing options that aren't relevant to their platform get what they want too. Here we restore the dependency but couple it with COMPILE_TEST, in order to achieve both of the above goals. [1] https://patchwork.kernel.org/patch/2194511/ Acked-by: Felipe Balbi Cc: Kishon Vijay Abraham I Cc: Moiz Sonasath Cc: Jiri Slaby Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 3622fff8b798..b57514ba486a 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -65,6 +65,7 @@ config NOP_USB_XCEIV config OMAP_CONTROL_USB tristate "OMAP CONTROL USB Driver" + depends on ARCH_OMAP2PLUS || COMPILE_TEST help Enable this to add support for the USB part present in the control module. This driver has API to power on the USB2 PHY and to write to @@ -84,6 +85,7 @@ config OMAP_USB2 config OMAP_USB3 tristate "OMAP USB3 PHY Driver" + depends on ARCH_OMAP2PLUS || COMPILE_TEST select OMAP_CONTROL_USB help Enable this to support the USB3 PHY that is part of SOC. This -- cgit v1.2.3-58-ga151 From 435932f2c75efcdd07273a437b9be32647d56d73 Mon Sep 17 00:00:00 2001 From: caizhiyong Date: Fri, 26 Jul 2013 07:12:14 +0000 Subject: USB: ohci_usb warn "irq nobody cared" on shutdown When ohci-hcd is shutting down, call ohci_usb_reset reset ohci-hcd, the root hub generate an interrupt, but ohci->rh_state is OHCI_RH_HALTED, and ohci_irq ignore the interrupt, the kernel trigger warning "irq nobody cared". ehci-hcd is first disable interrupts, then reset ehci. This patch disable ohci interrupt before reset ohci. The patch is tested at the arm cortex-a9 demo board. Signed-off-by: caizhiyong Reviewed-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index a9d3437da220..8f6b695af6a4 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -938,8 +938,8 @@ static void ohci_stop (struct usb_hcd *hcd) if (quirk_nec(ohci)) flush_work(&ohci->nec_work); - ohci_usb_reset (ohci); ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); + ohci_usb_reset(ohci); free_irq(hcd->irq, hcd); hcd->irq = 0; -- cgit v1.2.3-58-ga151 From 260eba39bcfb5681115ec934cdfbd97e0b3a1775 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 19 Jun 2013 17:38:09 +0200 Subject: usb: musb: replace ifndef with ifdef for CONFIG_MUSB_PIO_ONLY The ifdef reads somehow better than an ifndef Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dma.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 1b6b827b769f..8919ce28c3d4 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -62,10 +62,10 @@ struct musb_hw_ep; #define DMA_ADDR_INVALID (~(dma_addr_t)0) -#ifndef CONFIG_MUSB_PIO_ONLY -#define is_dma_capable() (1) -#else +#ifdef CONFIG_MUSB_PIO_ONLY #define is_dma_capable() (0) +#else +#define is_dma_capable() (1) #endif #ifdef CONFIG_USB_TI_CPPI_DMA -- cgit v1.2.3-58-ga151 From ff2283229da616d9a029eaa0d483fa8b0ad55e77 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 19 Jun 2013 17:38:10 +0200 Subject: usb: musb: musbhsdma: drop the controller check in dma_controller_destroy() This check is hardly required and alas is wrong. 'c' might be NULL but the chances are low that 'controller' after the container_of() becomes NULL. Since no other DMA implementation is doing that and musb-core does not call it with a NULL pointer it can dropped. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musbhsdma.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 3d1fd52a15a9..3f5e121dc154 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -371,9 +371,6 @@ void dma_controller_destroy(struct dma_controller *c) struct musb_dma_controller *controller = container_of(c, struct musb_dma_controller, controller); - if (!controller) - return; - if (controller->irq) free_irq(controller->irq, c); -- cgit v1.2.3-58-ga151 From 66c01883ef19bf4537b16931567b7d35c65356ad Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 19 Jun 2013 17:38:11 +0200 Subject: usb: musb: dma: merge ->start/stop into create/destroy The core code creates a controller and immediately after that it calls the ->start() callback. This one might drop an error but nobody cares. The same thing happens in the destroy corner: First ->stop() called followed by destroy callback. So why not merge those two into the same function since there is no difference. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/cppi_dma.c | 18 +++++------------- drivers/usb/musb/musb_core.c | 18 ++++-------------- drivers/usb/musb/musb_dma.h | 2 -- drivers/usb/musb/musbhsdma.c | 16 +++------------- drivers/usb/musb/tusb6010_omap.c | 24 ------------------------ drivers/usb/musb/ux500_dma.c | 19 ++++++++----------- 6 files changed, 20 insertions(+), 77 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 9db211ee15b5..904fb85d85a6 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -150,14 +150,11 @@ static void cppi_pool_free(struct cppi_channel *c) c->last_processed = NULL; } -static int cppi_controller_start(struct dma_controller *c) +static void cppi_controller_start(struct cppi *controller) { - struct cppi *controller; void __iomem *tibase; int i; - controller = container_of(c, struct cppi, controller); - /* do whatever is necessary to start controller */ for (i = 0; i < ARRAY_SIZE(controller->tx); i++) { controller->tx[i].transmit = true; @@ -212,8 +209,6 @@ static int cppi_controller_start(struct dma_controller *c) /* disable RNDIS mode, also host rx RNDIS autorequest */ musb_writel(tibase, DAVINCI_RNDIS_REG, 0); musb_writel(tibase, DAVINCI_AUTOREQ_REG, 0); - - return 0; } /* @@ -222,14 +217,12 @@ static int cppi_controller_start(struct dma_controller *c) * De-Init the DMA controller as necessary. */ -static int cppi_controller_stop(struct dma_controller *c) +static void cppi_controller_stop(struct cppi *controller) { - struct cppi *controller; void __iomem *tibase; int i; struct musb *musb; - controller = container_of(c, struct cppi, controller); musb = controller->musb; tibase = controller->tibase; @@ -255,8 +248,6 @@ static int cppi_controller_stop(struct dma_controller *c) /*disable tx/rx cppi */ musb_writel(tibase, DAVINCI_TXCPPI_CTRL_REG, DAVINCI_DMA_CTRL_DISABLE); musb_writel(tibase, DAVINCI_RXCPPI_CTRL_REG, DAVINCI_DMA_CTRL_DISABLE); - - return 0; } /* While dma channel is allocated, we only want the core irqs active @@ -1321,8 +1312,6 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr controller->tibase = mregs - DAVINCI_BASE_OFFSET; controller->musb = musb; - controller->controller.start = cppi_controller_start; - controller->controller.stop = cppi_controller_stop; controller->controller.channel_alloc = cppi_channel_allocate; controller->controller.channel_release = cppi_channel_release; controller->controller.channel_program = cppi_channel_program; @@ -1351,6 +1340,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr controller->irq = irq; } + cppi_controller_start(controller); return &controller->controller; } @@ -1363,6 +1353,8 @@ void dma_controller_destroy(struct dma_controller *c) cppi = container_of(c, struct cppi, controller); + cppi_controller_stop(cppi); + if (cppi->irq) free_irq(cppi->irq, cppi->musb); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 29a24ced6748..a4434d22ef13 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1764,12 +1764,8 @@ static void musb_free(struct musb *musb) disable_irq_wake(musb->nIrq); free_irq(musb->nIrq, musb); } - if (is_dma_capable() && musb->dma_controller) { - struct dma_controller *c = musb->dma_controller; - - (void) c->stop(c); - dma_controller_destroy(c); - } + if (is_dma_capable() && musb->dma_controller) + dma_controller_destroy(musb->dma_controller); musb_host_free(musb); } @@ -1845,14 +1841,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) pm_runtime_get_sync(musb->controller); #ifndef CONFIG_MUSB_PIO_ONLY - if (use_dma && dev->dma_mask) { - struct dma_controller *c; - - c = dma_controller_create(musb, musb->mregs); - musb->dma_controller = c; - if (c) - (void) c->start(c); - } + if (use_dma && dev->dma_mask) + musb->dma_controller = dma_controller_create(musb, musb->mregs); #endif /* ideally this would be abstracted in platform setup */ if (!is_dma_capable() || !musb->dma_controller) diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 8919ce28c3d4..36037114ad4a 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -159,8 +159,6 @@ dma_channel_status(struct dma_channel *c) * Controllers manage dma channels. */ struct dma_controller { - int (*start)(struct dma_controller *); - int (*stop)(struct dma_controller *); struct dma_channel *(*channel_alloc)(struct dma_controller *, struct musb_hw_ep *, u8 is_tx); void (*channel_release)(struct dma_channel *); diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 3f5e121dc154..e8e9f9aab203 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -37,18 +37,10 @@ #include "musb_core.h" #include "musbhsdma.h" -static int dma_controller_start(struct dma_controller *c) -{ - /* nothing to do */ - return 0; -} - static void dma_channel_release(struct dma_channel *channel); -static int dma_controller_stop(struct dma_controller *c) +static void dma_controller_stop(struct musb_dma_controller *controller) { - struct musb_dma_controller *controller = container_of(c, - struct musb_dma_controller, controller); struct musb *musb = controller->private_data; struct dma_channel *channel; u8 bit; @@ -67,8 +59,6 @@ static int dma_controller_stop(struct dma_controller *c) } } } - - return 0; } static struct dma_channel *dma_channel_allocate(struct dma_controller *c, @@ -371,6 +361,8 @@ void dma_controller_destroy(struct dma_controller *c) struct musb_dma_controller *controller = container_of(c, struct musb_dma_controller, controller); + dma_controller_stop(controller); + if (controller->irq) free_irq(controller->irq, c); @@ -397,8 +389,6 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba controller->private_data = musb; controller->base = base; - controller->controller.start = dma_controller_start; - controller->controller.stop = dma_controller_stop; controller->controller.channel_alloc = dma_channel_allocate; controller->controller.channel_release = dma_channel_release; controller->controller.channel_program = dma_channel_program; diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 98df17c984a8..b8794eb81e9c 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -66,28 +66,6 @@ struct tusb_omap_dma { unsigned multichannel:1; }; -static int tusb_omap_dma_start(struct dma_controller *c) -{ - struct tusb_omap_dma *tusb_dma; - - tusb_dma = container_of(c, struct tusb_omap_dma, controller); - - /* dev_dbg(musb->controller, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ - - return 0; -} - -static int tusb_omap_dma_stop(struct dma_controller *c) -{ - struct tusb_omap_dma *tusb_dma; - - tusb_dma = container_of(c, struct tusb_omap_dma, controller); - - /* dev_dbg(musb->controller, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ - - return 0; -} - /* * Allocate dmareq0 to the current channel unless it's already taken */ @@ -695,8 +673,6 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba tusb_dma->dmareq = -1; tusb_dma->sync_dev = -1; - tusb_dma->controller.start = tusb_omap_dma_start; - tusb_dma->controller.stop = tusb_omap_dma_stop; tusb_dma->controller.channel_alloc = tusb_omap_dma_allocate; tusb_dma->controller.channel_release = tusb_omap_dma_release; tusb_dma->controller.channel_program = tusb_omap_dma_program; diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index bfb7a65d83cc..1ce214e5b7ad 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -254,10 +254,8 @@ static int ux500_dma_channel_abort(struct dma_channel *channel) return 0; } -static int ux500_dma_controller_stop(struct dma_controller *c) +static void ux500_dma_controller_stop(struct ux500_dma_controller *controller) { - struct ux500_dma_controller *controller = container_of(c, - struct ux500_dma_controller, controller); struct ux500_dma_channel *ux500_channel; struct dma_channel *channel; u8 ch_num; @@ -281,14 +279,10 @@ static int ux500_dma_controller_stop(struct dma_controller *c) if (ux500_channel->dma_chan) dma_release_channel(ux500_channel->dma_chan); } - - return 0; } -static int ux500_dma_controller_start(struct dma_controller *c) +static int ux500_dma_controller_start(struct ux500_dma_controller *controller) { - struct ux500_dma_controller *controller = container_of(c, - struct ux500_dma_controller, controller); struct ux500_dma_channel *ux500_channel = NULL; struct musb *musb = controller->private_data; struct device *dev = musb->controller; @@ -347,7 +341,7 @@ static int ux500_dma_controller_start(struct dma_controller *c) dir, ch_num); /* Release already allocated channels */ - ux500_dma_controller_stop(c); + ux500_dma_controller_stop(controller); return -EBUSY; } @@ -369,6 +363,7 @@ void dma_controller_destroy(struct dma_controller *c) struct ux500_dma_controller *controller = container_of(c, struct ux500_dma_controller, controller); + ux500_dma_controller_stop(controller); kfree(controller); } @@ -378,6 +373,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, struct ux500_dma_controller *controller; struct platform_device *pdev = to_platform_device(musb->controller); struct resource *iomem; + int ret; controller = kzalloc(sizeof(*controller), GFP_KERNEL); if (!controller) @@ -394,14 +390,15 @@ struct dma_controller *dma_controller_create(struct musb *musb, controller->phy_base = (dma_addr_t) iomem->start; - controller->controller.start = ux500_dma_controller_start; - controller->controller.stop = ux500_dma_controller_stop; controller->controller.channel_alloc = ux500_dma_channel_allocate; controller->controller.channel_release = ux500_dma_channel_release; controller->controller.channel_program = ux500_dma_channel_program; controller->controller.channel_abort = ux500_dma_channel_abort; controller->controller.is_compatible = ux500_dma_is_compatible; + ret = ux500_dma_controller_start(controller); + if (ret) + goto plat_get_fail; return &controller->controller; plat_get_fail: -- cgit v1.2.3-58-ga151 From a6a20885f1e13340e30ec35acfdcf14756ecf03c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 19 Jun 2013 17:38:12 +0200 Subject: usb: musb: provide empty dma_controller_create() in PIO mode Add a dma_controller_create() returning NULL so a few ifdefs can dropped. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dma.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 36037114ad4a..c8e67fde2156 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -175,9 +175,20 @@ struct dma_controller { /* called after channel_program(), may indicate a fault */ extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit); +#ifdef CONFIG_MUSB_PIO_ONLY +static inline struct dma_controller *dma_controller_create(struct musb *m, + void __iomem *io) +{ + return NULL; +} + +static inline void dma_controller_destroy(struct dma_controller *d) { } + +#else extern struct dma_controller *dma_controller_create(struct musb *, void __iomem *); extern void dma_controller_destroy(struct dma_controller *); +#endif #endif /* __MUSB_DMA_H__ */ -- cgit v1.2.3-58-ga151 From 6904b845e27830ea0dc8d00fada5a3f478924ea3 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 19 Jun 2013 17:38:13 +0200 Subject: usb: musb: remove a few is_dma_capable() in init/exit code This patch removes is_dma_capable() and an ifdef in the init/exit path around init/de-init of the dma_controller. Since we have the empty stubs in the PIO code we can call it without gcc trouble. Earlier we had an ifdef and the is_dma_capable() macro where gcc ignored the if (0) path even that the function was not around :) Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index a4434d22ef13..b33bed5b0dc8 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1764,7 +1764,7 @@ static void musb_free(struct musb *musb) disable_irq_wake(musb->nIrq); free_irq(musb->nIrq, musb); } - if (is_dma_capable() && musb->dma_controller) + if (musb->dma_controller) dma_controller_destroy(musb->dma_controller); musb_host_free(musb); @@ -1840,12 +1840,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) pm_runtime_get_sync(musb->controller); -#ifndef CONFIG_MUSB_PIO_ONLY if (use_dma && dev->dma_mask) musb->dma_controller = dma_controller_create(musb, musb->mregs); -#endif + /* ideally this would be abstracted in platform setup */ - if (!is_dma_capable() || !musb->dma_controller) + if (!musb->dma_controller) dev->dma_mask = NULL; /* be sure interrupts are disabled before connecting ISR */ -- cgit v1.2.3-58-ga151 From f3ce4d5b2d8d714407a646c07505c5174afd7574 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 19 Jun 2013 17:38:14 +0200 Subject: usb: musb: core: call dma_controller_destroy() in the err path The cleanup in the error is missing the dma controller. The structure is allocated at runtime and ux500 allocates even a little more than just this struct. So cleanup! Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b33bed5b0dc8..9b774e72c0e4 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1933,6 +1933,8 @@ fail4: musb_gadget_cleanup(musb); fail3: + if (musb->dma_controller) + dma_controller_destroy(musb->dma_controller); pm_runtime_put_sync(musb->controller); fail2: -- cgit v1.2.3-58-ga151 From 23a53d9008251825a4c8eea64503ed676e1d0f04 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 19 Jun 2013 17:38:15 +0200 Subject: usb: musb: unmap reqs in musb_gadget_queue()'s error case If the descriptor is missing the reqeust is never unmapped. This patch changes this and renames the cleanup label to unlock since there is no cleanup done. The cleanup would revert the allocation of ressource (i.e. this dma mapping) but it does not, it simply unlocks and returns. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 0414bc19d009..96632f9e3eeb 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1266,7 +1266,8 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, dev_dbg(musb->controller, "req %p queued to %s while ep %s\n", req, ep->name, "disabled"); status = -ESHUTDOWN; - goto cleanup; + unmap_dma_buffer(request, musb); + goto unlock; } /* add request to the list */ @@ -1276,7 +1277,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, if (!musb_ep->busy && &request->list == musb_ep->req_list.next) musb_ep_restart(musb, request); -cleanup: +unlock: spin_unlock_irqrestore(&musb->lock, lockflags); return status; } -- cgit v1.2.3-58-ga151 From 1a356dbc6465b0a87474993dafcd7a101ef57ff9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 26 Jun 2013 15:14:06 +0300 Subject: usb: dwc3: make glue layers selectable Glue layers are starting to have separate requirements. For example, OMAP's glue layer is starting to use extcon framework which no one else needs. In order to make it clear the proper dependencies, we are now allowing glue layers to be selectable so that each glue layer can list their own dependencies without messing with the core IP driver. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 32 ++++++++++++++++++++++++++++++++ drivers/usb/dwc3/Makefile | 13 +++---------- 2 files changed, 35 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 2378958ea63e..3e225d5846f6 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -40,6 +40,38 @@ config USB_DWC3_DUAL_ROLE endchoice +comment "Platform Glue Driver Support" + +config USB_DWC3_OMAP + tristate "Texas Instruments OMAP5 and similar Platforms" + depends on EXTCON + default USB_DWC3 + help + Some platforms from Texas Instruments like OMAP5, DRA7xxx and + AM437x use this IP for USB2/3 functionality. + + Say 'Y' or 'M' here if you have one such device + +config USB_DWC3_EXYNOS + tristate "Samsung Exynos Platform" + default USB_DWC3 + help + Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside, + say 'Y' or 'M' if you have one such device. + +config USB_DWC3_PCI + tristate "PCIe-based Platforms" + depends on PCI + default USB_DWC3 + help + If you're using the DesignWare Core IP with a PCIe, please say + 'Y' or 'M' here. + + One such PCIe-based platform is Synopsys' PCIe HAPS model of + this IP. + +comment "Debugging features" + config USB_DWC3_DEBUG bool "Enable Debugging Messages" help diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 0c7ac92582be..dd1760145c46 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -27,15 +27,8 @@ endif # the entire driver (with all its glue layers) on several architectures # and make sure it compiles fine. This will also help with allmodconfig # and allyesconfig builds. -# -# The only exception is the PCI glue layer, but that's only because -# PCI doesn't provide nops if CONFIG_PCI isn't enabled. ## -obj-$(CONFIG_USB_DWC3) += dwc3-omap.o -obj-$(CONFIG_USB_DWC3) += dwc3-exynos.o - -ifneq ($(CONFIG_PCI),) - obj-$(CONFIG_USB_DWC3) += dwc3-pci.o -endif - +obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o +obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o +obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o -- cgit v1.2.3-58-ga151 From 2b0c49530bc5fdbde93295e5c8f26f55d0de1e04 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 27 Jun 2013 10:20:44 +0300 Subject: usb: gadget: remove imx_udc That driver hasn't been really maintained for a long time. It doesn't compile in any way, it includes non-existent headers, has no users, and is just plain broken. The person who used to work with that driver has publicly stated that he has no plans to touch that driver again and is ok with removal[1]. Due to these factors, imx_udc is now removed from the tree, if someone really believe it needs to be kept, please fix the bugs in that driver. [1] http://marc.info/?l=linux-usb&m=136197620417636&w=2 Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 15 - drivers/usb/gadget/Makefile | 1 - drivers/usb/gadget/imx_udc.c | 1544 ------------------------------------------ drivers/usb/gadget/imx_udc.h | 351 ---------- 4 files changed, 1911 deletions(-) delete mode 100644 drivers/usb/gadget/imx_udc.c delete mode 100644 drivers/usb/gadget/imx_udc.h (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 8e9368330b10..08dab3c640fd 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -287,21 +287,6 @@ config USB_S3C_HSOTG The Samsung S3C64XX USB2.0 high-speed gadget controller integrated into the S3C64XX series SoC. -config USB_IMX - tristate "Freescale i.MX1 USB Peripheral Controller" - depends on ARCH_MXC - depends on BROKEN - help - Freescale's i.MX1 includes an integrated full speed - USB 1.1 device controller. - - It has Six fixed-function endpoints, as well as endpoint - zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "imx_udc" and force all - gadget drivers to also be dynamically linked. - config USB_S3C2410 tristate "S3C2410 USB Device Controller" depends on ARCH_S3C24XX diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index bad08e66f369..386db9daf1d9 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o -obj-$(CONFIG_USB_IMX) += imx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c deleted file mode 100644 index 9b2d24e4c95f..000000000000 --- a/drivers/usb/gadget/imx_udc.c +++ /dev/null @@ -1,1544 +0,0 @@ -/* - * driver/usb/gadget/imx_udc.c - * - * Copyright (C) 2005 Mike Lee - * Copyright (C) 2008 Darius Augulis - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "imx_udc.h" - -static const char driver_name[] = "imx_udc"; -static const char ep0name[] = "ep0"; - -void ep0_chg_stat(const char *label, struct imx_udc_struct *imx_usb, - enum ep0_state stat); - -/******************************************************************************* - * IMX UDC hardware related functions - ******************************************************************************* - */ - -void imx_udc_enable(struct imx_udc_struct *imx_usb) -{ - int temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA, - imx_usb->base + USB_CTRL); - imx_usb->gadget.speed = USB_SPEED_FULL; -} - -void imx_udc_disable(struct imx_udc_struct *imx_usb) -{ - int temp = __raw_readl(imx_usb->base + USB_CTRL); - - __raw_writel(temp & ~(CTRL_FE_ENA | CTRL_AFE_ENA), - imx_usb->base + USB_CTRL); - - ep0_chg_stat(__func__, imx_usb, EP0_IDLE); - imx_usb->gadget.speed = USB_SPEED_UNKNOWN; -} - -void imx_udc_reset(struct imx_udc_struct *imx_usb) -{ - int temp = __raw_readl(imx_usb->base + USB_ENAB); - - /* set RST bit */ - __raw_writel(temp | ENAB_RST, imx_usb->base + USB_ENAB); - - /* wait RST bit to clear */ - do {} while (__raw_readl(imx_usb->base + USB_ENAB) & ENAB_RST); - - /* wait CFG bit to assert */ - do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); - - /* udc module is now ready */ -} - -void imx_udc_config(struct imx_udc_struct *imx_usb) -{ - u8 ep_conf[5]; - u8 i, j, cfg; - struct imx_ep_struct *imx_ep; - - /* wait CFG bit to assert */ - do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); - - /* Download the endpoint buffer for endpoint 0. */ - for (j = 0; j < 5; j++) { - i = (j == 2 ? imx_usb->imx_ep[0].fifosize : 0x00); - __raw_writeb(i, imx_usb->base + USB_DDAT); - do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_BSY); - } - - /* Download the endpoint buffers for endpoints 1-5. - * We specify two configurations, one interface - */ - for (cfg = 1; cfg < 3; cfg++) { - for (i = 1; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - /* EP no | Config no */ - ep_conf[0] = (i << 4) | (cfg << 2); - /* Type | Direction */ - ep_conf[1] = (imx_ep->bmAttributes << 3) | - (EP_DIR(imx_ep) << 2); - /* Max packet size */ - ep_conf[2] = imx_ep->fifosize; - /* TRXTYP */ - ep_conf[3] = 0xC0; - /* FIFO no */ - ep_conf[4] = i; - - D_INI(imx_usb->dev, - "<%s> ep%d_conf[%d]:" - "[%02x-%02x-%02x-%02x-%02x]\n", - __func__, i, cfg, - ep_conf[0], ep_conf[1], ep_conf[2], - ep_conf[3], ep_conf[4]); - - for (j = 0; j < 5; j++) { - __raw_writeb(ep_conf[j], - imx_usb->base + USB_DDAT); - do {} while (__raw_readl(imx_usb->base - + USB_DADR) - & DADR_BSY); - } - } - } - - /* wait CFG bit to clear */ - do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG); -} - -void imx_udc_init_irq(struct imx_udc_struct *imx_usb) -{ - int i; - - /* Mask and clear all irqs */ - __raw_writel(0xFFFFFFFF, imx_usb->base + USB_MASK); - __raw_writel(0xFFFFFFFF, imx_usb->base + USB_INTR); - for (i = 0; i < IMX_USB_NB_EP; i++) { - __raw_writel(0x1FF, imx_usb->base + USB_EP_MASK(i)); - __raw_writel(0x1FF, imx_usb->base + USB_EP_INTR(i)); - } - - /* Enable USB irqs */ - __raw_writel(INTR_MSOF | INTR_FRAME_MATCH, imx_usb->base + USB_MASK); - - /* Enable EP0 irqs */ - __raw_writel(0x1FF & ~(EPINTR_DEVREQ | EPINTR_MDEVREQ | EPINTR_EOT - | EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL), - imx_usb->base + USB_EP_MASK(0)); -} - -void imx_udc_init_ep(struct imx_udc_struct *imx_usb) -{ - int i, max, temp; - struct imx_ep_struct *imx_ep; - for (i = 0; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - switch (imx_ep->fifosize) { - case 8: - max = 0; - break; - case 16: - max = 1; - break; - case 32: - max = 2; - break; - case 64: - max = 3; - break; - default: - max = 1; - break; - } - temp = (EP_DIR(imx_ep) << 7) | (max << 5) - | (imx_ep->bmAttributes << 3); - __raw_writel(temp, imx_usb->base + USB_EP_STAT(i)); - __raw_writel(temp | EPSTAT_FLUSH, - imx_usb->base + USB_EP_STAT(i)); - D_INI(imx_usb->dev, "<%s> ep%d_stat %08x\n", __func__, i, - __raw_readl(imx_usb->base + USB_EP_STAT(i))); - } -} - -void imx_udc_init_fifo(struct imx_udc_struct *imx_usb) -{ - int i, temp; - struct imx_ep_struct *imx_ep; - for (i = 0; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - - /* Fifo control */ - temp = EP_DIR(imx_ep) ? 0x0B000000 : 0x0F000000; - __raw_writel(temp, imx_usb->base + USB_EP_FCTRL(i)); - D_INI(imx_usb->dev, "<%s> ep%d_fctrl %08x\n", __func__, i, - __raw_readl(imx_usb->base + USB_EP_FCTRL(i))); - - /* Fifo alarm */ - temp = (i ? imx_ep->fifosize / 2 : 0); - __raw_writel(temp, imx_usb->base + USB_EP_FALRM(i)); - D_INI(imx_usb->dev, "<%s> ep%d_falrm %08x\n", __func__, i, - __raw_readl(imx_usb->base + USB_EP_FALRM(i))); - } -} - -static void imx_udc_init(struct imx_udc_struct *imx_usb) -{ - /* Reset UDC */ - imx_udc_reset(imx_usb); - - /* Download config to enpoint buffer */ - imx_udc_config(imx_usb); - - /* Setup interrups */ - imx_udc_init_irq(imx_usb); - - /* Setup endpoints */ - imx_udc_init_ep(imx_usb); - - /* Setup fifos */ - imx_udc_init_fifo(imx_usb); -} - -void imx_ep_irq_enable(struct imx_ep_struct *imx_ep) -{ - - int i = EP_NO(imx_ep); - - __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); - __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); - __raw_writel(0x1FF & ~(EPINTR_EOT | EPINTR_EOF), - imx_ep->imx_usb->base + USB_EP_MASK(i)); -} - -void imx_ep_irq_disable(struct imx_ep_struct *imx_ep) -{ - - int i = EP_NO(imx_ep); - - __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); - __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); -} - -int imx_ep_empty(struct imx_ep_struct *imx_ep) -{ - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - - return __raw_readl(imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) - & FSTAT_EMPTY; -} - -unsigned imx_fifo_bcount(struct imx_ep_struct *imx_ep) -{ - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - - return (__raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))) - & EPSTAT_BCOUNT) >> 16; -} - -void imx_flush(struct imx_ep_struct *imx_ep) -{ - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - - int temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); - __raw_writel(temp | EPSTAT_FLUSH, - imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); -} - -void imx_ep_stall(struct imx_ep_struct *imx_ep) -{ - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - int temp, i; - - D_ERR(imx_usb->dev, - "<%s> Forced stall on %s\n", __func__, imx_ep->ep.name); - - imx_flush(imx_ep); - - /* Special care for ep0 */ - if (!EP_NO(imx_ep)) { - temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR, - imx_usb->base + USB_CTRL); - do { } while (__raw_readl(imx_usb->base + USB_CTRL) - & CTRL_CMDOVER); - temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp & ~CTRL_CMDERROR, imx_usb->base + USB_CTRL); - } - else { - temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); - __raw_writel(temp | EPSTAT_STALL, - imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); - - for (i = 0; i < 100; i ++) { - temp = __raw_readl(imx_usb->base - + USB_EP_STAT(EP_NO(imx_ep))); - if (!(temp & EPSTAT_STALL)) - break; - udelay(20); - } - if (i == 100) - D_ERR(imx_usb->dev, "<%s> Non finished stall on %s\n", - __func__, imx_ep->ep.name); - } -} - -static int imx_udc_get_frame(struct usb_gadget *_gadget) -{ - struct imx_udc_struct *imx_usb = container_of(_gadget, - struct imx_udc_struct, gadget); - - return __raw_readl(imx_usb->base + USB_FRAME) & 0x7FF; -} - -static int imx_udc_wakeup(struct usb_gadget *_gadget) -{ - return 0; -} - -/******************************************************************************* - * USB request control functions - ******************************************************************************* - */ - -static void ep_add_request(struct imx_ep_struct *imx_ep, - struct imx_request *req) -{ - if (unlikely(!req)) - return; - - req->in_use = 1; - list_add_tail(&req->queue, &imx_ep->queue); -} - -static void ep_del_request(struct imx_ep_struct *imx_ep, - struct imx_request *req) -{ - if (unlikely(!req)) - return; - - list_del_init(&req->queue); - req->in_use = 0; -} - -static void done(struct imx_ep_struct *imx_ep, - struct imx_request *req, int status) -{ - ep_del_request(imx_ep, req); - - if (likely(req->req.status == -EINPROGRESS)) - req->req.status = status; - else - status = req->req.status; - - if (status && status != -ESHUTDOWN) - D_ERR(imx_ep->imx_usb->dev, - "<%s> complete %s req %p stat %d len %u/%u\n", __func__, - imx_ep->ep.name, &req->req, status, - req->req.actual, req->req.length); - - req->req.complete(&imx_ep->ep, &req->req); -} - -static void nuke(struct imx_ep_struct *imx_ep, int status) -{ - struct imx_request *req; - - while (!list_empty(&imx_ep->queue)) { - req = list_entry(imx_ep->queue.next, struct imx_request, queue); - done(imx_ep, req, status); - } -} - -/******************************************************************************* - * Data tansfer over USB functions - ******************************************************************************* - */ -static int read_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) -{ - u8 *buf; - int bytes_ep, bufferspace, count, i; - - bytes_ep = imx_fifo_bcount(imx_ep); - bufferspace = req->req.length - req->req.actual; - - buf = req->req.buf + req->req.actual; - prefetchw(buf); - - if (unlikely(imx_ep_empty(imx_ep))) - count = 0; /* zlp */ - else - count = min(bytes_ep, bufferspace); - - for (i = count; i > 0; i--) - *buf++ = __raw_readb(imx_ep->imx_usb->base - + USB_EP_FDAT0(EP_NO(imx_ep))); - req->req.actual += count; - - return count; -} - -static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) -{ - u8 *buf; - int length, count, temp; - - if (unlikely(__raw_readl(imx_ep->imx_usb->base + - USB_EP_STAT(EP_NO(imx_ep))) & EPSTAT_ZLPS)) { - D_TRX(imx_ep->imx_usb->dev, "<%s> zlp still queued in EP %s\n", - __func__, imx_ep->ep.name); - return -1; - } - - buf = req->req.buf + req->req.actual; - prefetch(buf); - - length = min(req->req.length - req->req.actual, (u32)imx_ep->fifosize); - - if (imx_fifo_bcount(imx_ep) + length > imx_ep->fifosize) { - D_TRX(imx_ep->imx_usb->dev, "<%s> packet overfill %s fifo\n", - __func__, imx_ep->ep.name); - return -1; - } - - req->req.actual += length; - count = length; - - if (!count && req->req.zero) { /* zlp */ - temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_STAT(EP_NO(imx_ep))); - __raw_writel(temp | EPSTAT_ZLPS, imx_ep->imx_usb->base - + USB_EP_STAT(EP_NO(imx_ep))); - D_TRX(imx_ep->imx_usb->dev, "<%s> zero packet\n", __func__); - return 0; - } - - while (count--) { - if (count == 0) { /* last byte */ - temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_FCTRL(EP_NO(imx_ep))); - __raw_writel(temp | FCTRL_WFR, imx_ep->imx_usb->base - + USB_EP_FCTRL(EP_NO(imx_ep))); - } - __raw_writeb(*buf++, - imx_ep->imx_usb->base + USB_EP_FDAT0(EP_NO(imx_ep))); - } - - return length; -} - -static int read_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) -{ - int bytes = 0, - count, - completed = 0; - - while (__raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) - & FSTAT_FR) { - count = read_packet(imx_ep, req); - bytes += count; - - completed = (count != imx_ep->fifosize); - if (completed || req->req.actual == req->req.length) { - completed = 1; - break; - } - } - - if (completed || !req->req.length) { - done(imx_ep, req, 0); - D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n", - __func__, imx_ep->ep.name, req, - completed ? "completed" : "not completed"); - if (!EP_NO(imx_ep)) - ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE); - } - - D_TRX(imx_ep->imx_usb->dev, "<%s> bytes read: %d\n", __func__, bytes); - - return completed; -} - -static int write_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) -{ - int bytes = 0, - count, - completed = 0; - - while (!completed) { - count = write_packet(imx_ep, req); - if (count < 0) - break; /* busy */ - bytes += count; - - /* last packet "must be" short (or a zlp) */ - completed = (count != imx_ep->fifosize); - - if (unlikely(completed)) { - done(imx_ep, req, 0); - D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n", - __func__, imx_ep->ep.name, req, - completed ? "completed" : "not completed"); - if (!EP_NO(imx_ep)) - ep0_chg_stat(__func__, - imx_ep->imx_usb, EP0_IDLE); - } - } - - D_TRX(imx_ep->imx_usb->dev, "<%s> bytes sent: %d\n", __func__, bytes); - - return completed; -} - -/******************************************************************************* - * Endpoint handlers - ******************************************************************************* - */ -static int handle_ep(struct imx_ep_struct *imx_ep) -{ - struct imx_request *req; - int completed = 0; - - do { - if (!list_empty(&imx_ep->queue)) - req = list_entry(imx_ep->queue.next, - struct imx_request, queue); - else { - D_REQ(imx_ep->imx_usb->dev, "<%s> no request on %s\n", - __func__, imx_ep->ep.name); - return 0; - } - - if (EP_DIR(imx_ep)) /* to host */ - completed = write_fifo(imx_ep, req); - else /* to device */ - completed = read_fifo(imx_ep, req); - - dump_ep_stat(__func__, imx_ep); - - } while (completed); - - return 0; -} - -static int handle_ep0(struct imx_ep_struct *imx_ep) -{ - struct imx_request *req = NULL; - int ret = 0; - - if (!list_empty(&imx_ep->queue)) { - req = list_entry(imx_ep->queue.next, struct imx_request, queue); - - switch (imx_ep->imx_usb->ep0state) { - - case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR */ - write_fifo(imx_ep, req); - break; - case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR */ - read_fifo(imx_ep, req); - break; - default: - D_EP0(imx_ep->imx_usb->dev, - "<%s> ep0 i/o, odd state %d\n", - __func__, imx_ep->imx_usb->ep0state); - ep_del_request(imx_ep, req); - ret = -EL2HLT; - break; - } - } - - else - D_ERR(imx_ep->imx_usb->dev, "<%s> no request on %s\n", - __func__, imx_ep->ep.name); - - return ret; -} - -static void handle_ep0_devreq(struct imx_udc_struct *imx_usb) -{ - struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0]; - union { - struct usb_ctrlrequest r; - u8 raw[8]; - u32 word[2]; - } u; - int temp, i; - - nuke(imx_ep, -EPROTO); - - /* read SETUP packet */ - for (i = 0; i < 2; i++) { - if (imx_ep_empty(imx_ep)) { - D_ERR(imx_usb->dev, - "<%s> no setup packet received\n", __func__); - goto stall; - } - u.word[i] = __raw_readl(imx_usb->base - + USB_EP_FDAT(EP_NO(imx_ep))); - } - - temp = imx_ep_empty(imx_ep); - while (!imx_ep_empty(imx_ep)) { - i = __raw_readl(imx_usb->base + USB_EP_FDAT(EP_NO(imx_ep))); - D_ERR(imx_usb->dev, - "<%s> wrong to have extra bytes for setup : 0x%08x\n", - __func__, i); - } - if (!temp) - goto stall; - - le16_to_cpus(&u.r.wValue); - le16_to_cpus(&u.r.wIndex); - le16_to_cpus(&u.r.wLength); - - D_REQ(imx_usb->dev, "<%s> SETUP %02x.%02x v%04x i%04x l%04x\n", - __func__, u.r.bRequestType, u.r.bRequest, - u.r.wValue, u.r.wIndex, u.r.wLength); - - if (imx_usb->set_config) { - /* NACK the host by using CMDOVER */ - temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL); - - D_ERR(imx_usb->dev, - "<%s> set config req is pending, NACK the host\n", - __func__); - return; - } - - if (u.r.bRequestType & USB_DIR_IN) - ep0_chg_stat(__func__, imx_usb, EP0_IN_DATA_PHASE); - else - ep0_chg_stat(__func__, imx_usb, EP0_OUT_DATA_PHASE); - - i = imx_usb->driver->setup(&imx_usb->gadget, &u.r); - if (i < 0) { - D_ERR(imx_usb->dev, "<%s> device setup error %d\n", - __func__, i); - goto stall; - } - - return; -stall: - D_ERR(imx_usb->dev, "<%s> protocol STALL\n", __func__); - imx_ep_stall(imx_ep); - ep0_chg_stat(__func__, imx_usb, EP0_STALL); - return; -} - -/******************************************************************************* - * USB gadget callback functions - ******************************************************************************* - */ - -static int imx_ep_enable(struct usb_ep *usb_ep, - const struct usb_endpoint_descriptor *desc) -{ - struct imx_ep_struct *imx_ep = container_of(usb_ep, - struct imx_ep_struct, ep); - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - unsigned long flags; - - if (!usb_ep - || !desc - || !EP_NO(imx_ep) - || desc->bDescriptorType != USB_DT_ENDPOINT - || imx_ep->bEndpointAddress != desc->bEndpointAddress) { - D_ERR(imx_usb->dev, - "<%s> bad ep or descriptor\n", __func__); - return -EINVAL; - } - - if (imx_ep->bmAttributes != desc->bmAttributes) { - D_ERR(imx_usb->dev, - "<%s> %s type mismatch\n", __func__, usb_ep->name); - return -EINVAL; - } - - if (imx_ep->fifosize < usb_endpoint_maxp(desc)) { - D_ERR(imx_usb->dev, - "<%s> bad %s maxpacket\n", __func__, usb_ep->name); - return -ERANGE; - } - - if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { - D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__); - return -ESHUTDOWN; - } - - local_irq_save(flags); - - imx_ep->stopped = 0; - imx_flush(imx_ep); - imx_ep_irq_enable(imx_ep); - - local_irq_restore(flags); - - D_EPX(imx_usb->dev, "<%s> ENABLED %s\n", __func__, usb_ep->name); - return 0; -} - -static int imx_ep_disable(struct usb_ep *usb_ep) -{ - struct imx_ep_struct *imx_ep = container_of(usb_ep, - struct imx_ep_struct, ep); - unsigned long flags; - - if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { - D_ERR(imx_ep->imx_usb->dev, "<%s> %s can not be disabled\n", - __func__, usb_ep ? imx_ep->ep.name : NULL); - return -EINVAL; - } - - local_irq_save(flags); - - imx_ep->stopped = 1; - nuke(imx_ep, -ESHUTDOWN); - imx_flush(imx_ep); - imx_ep_irq_disable(imx_ep); - - local_irq_restore(flags); - - D_EPX(imx_ep->imx_usb->dev, - "<%s> DISABLED %s\n", __func__, usb_ep->name); - return 0; -} - -static struct usb_request *imx_ep_alloc_request - (struct usb_ep *usb_ep, gfp_t gfp_flags) -{ - struct imx_request *req; - - if (!usb_ep) - return NULL; - - req = kzalloc(sizeof *req, gfp_flags); - if (!req) - return NULL; - - INIT_LIST_HEAD(&req->queue); - req->in_use = 0; - - return &req->req; -} - -static void imx_ep_free_request - (struct usb_ep *usb_ep, struct usb_request *usb_req) -{ - struct imx_request *req; - - req = container_of(usb_req, struct imx_request, req); - WARN_ON(!list_empty(&req->queue)); - kfree(req); -} - -static int imx_ep_queue - (struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags) -{ - struct imx_ep_struct *imx_ep; - struct imx_udc_struct *imx_usb; - struct imx_request *req; - unsigned long flags; - int ret = 0; - - imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); - imx_usb = imx_ep->imx_usb; - req = container_of(usb_req, struct imx_request, req); - - /* - Special care on IMX udc. - Ignore enqueue when after set configuration from the - host. This assume all gadget drivers reply set - configuration with the next ep0 req enqueue. - */ - if (imx_usb->set_config && !EP_NO(imx_ep)) { - imx_usb->set_config = 0; - D_ERR(imx_usb->dev, - "<%s> gadget reply set config\n", __func__); - return 0; - } - - if (unlikely(!usb_req || !req || !usb_req->complete || !usb_req->buf)) { - D_ERR(imx_usb->dev, "<%s> bad params\n", __func__); - return -EINVAL; - } - - if (unlikely(!usb_ep || !imx_ep)) { - D_ERR(imx_usb->dev, "<%s> bad ep\n", __func__); - return -EINVAL; - } - - if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { - D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__); - return -ESHUTDOWN; - } - - /* Debug */ - D_REQ(imx_usb->dev, "<%s> ep%d %s request for [%d] bytes\n", - __func__, EP_NO(imx_ep), - ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state - == EP0_IN_DATA_PHASE) - || (EP_NO(imx_ep) && EP_DIR(imx_ep))) - ? "IN" : "OUT", usb_req->length); - dump_req(__func__, imx_ep, usb_req); - - if (imx_ep->stopped) { - usb_req->status = -ESHUTDOWN; - return -ESHUTDOWN; - } - - if (req->in_use) { - D_ERR(imx_usb->dev, - "<%s> refusing to queue req %p (already queued)\n", - __func__, req); - return 0; - } - - local_irq_save(flags); - - usb_req->status = -EINPROGRESS; - usb_req->actual = 0; - - ep_add_request(imx_ep, req); - - if (!EP_NO(imx_ep)) - ret = handle_ep0(imx_ep); - else - ret = handle_ep(imx_ep); - - local_irq_restore(flags); - return ret; -} - -static int imx_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) -{ - - struct imx_ep_struct *imx_ep = container_of - (usb_ep, struct imx_ep_struct, ep); - struct imx_request *req; - unsigned long flags; - - if (unlikely(!usb_ep || !EP_NO(imx_ep))) { - D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); - return -EINVAL; - } - - local_irq_save(flags); - - /* make sure it's actually queued on this endpoint */ - list_for_each_entry(req, &imx_ep->queue, queue) { - if (&req->req == usb_req) - break; - } - if (&req->req != usb_req) { - local_irq_restore(flags); - return -EINVAL; - } - - done(imx_ep, req, -ECONNRESET); - - local_irq_restore(flags); - return 0; -} - -static int imx_ep_set_halt(struct usb_ep *usb_ep, int value) -{ - struct imx_ep_struct *imx_ep = container_of - (usb_ep, struct imx_ep_struct, ep); - unsigned long flags; - - if (unlikely(!usb_ep || !EP_NO(imx_ep))) { - D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); - return -EINVAL; - } - - local_irq_save(flags); - - if ((imx_ep->bEndpointAddress & USB_DIR_IN) - && !list_empty(&imx_ep->queue)) { - local_irq_restore(flags); - return -EAGAIN; - } - - imx_ep_stall(imx_ep); - - local_irq_restore(flags); - - D_EPX(imx_ep->imx_usb->dev, "<%s> %s halt\n", __func__, usb_ep->name); - return 0; -} - -static int imx_ep_fifo_status(struct usb_ep *usb_ep) -{ - struct imx_ep_struct *imx_ep = container_of - (usb_ep, struct imx_ep_struct, ep); - - if (!usb_ep) { - D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); - return -ENODEV; - } - - if (imx_ep->imx_usb->gadget.speed == USB_SPEED_UNKNOWN) - return 0; - else - return imx_fifo_bcount(imx_ep); -} - -static void imx_ep_fifo_flush(struct usb_ep *usb_ep) -{ - struct imx_ep_struct *imx_ep = container_of - (usb_ep, struct imx_ep_struct, ep); - unsigned long flags; - - local_irq_save(flags); - - if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { - D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); - local_irq_restore(flags); - return; - } - - /* toggle and halt bits stay unchanged */ - imx_flush(imx_ep); - - local_irq_restore(flags); -} - -static struct usb_ep_ops imx_ep_ops = { - .enable = imx_ep_enable, - .disable = imx_ep_disable, - - .alloc_request = imx_ep_alloc_request, - .free_request = imx_ep_free_request, - - .queue = imx_ep_queue, - .dequeue = imx_ep_dequeue, - - .set_halt = imx_ep_set_halt, - .fifo_status = imx_ep_fifo_status, - .fifo_flush = imx_ep_fifo_flush, -}; - -/******************************************************************************* - * USB endpoint control functions - ******************************************************************************* - */ - -void ep0_chg_stat(const char *label, - struct imx_udc_struct *imx_usb, enum ep0_state stat) -{ - D_EP0(imx_usb->dev, "<%s> from %15s to %15s\n", - label, state_name[imx_usb->ep0state], state_name[stat]); - - if (imx_usb->ep0state == stat) - return; - - imx_usb->ep0state = stat; -} - -static void usb_init_data(struct imx_udc_struct *imx_usb) -{ - struct imx_ep_struct *imx_ep; - u8 i; - - /* device/ep0 records init */ - INIT_LIST_HEAD(&imx_usb->gadget.ep_list); - INIT_LIST_HEAD(&imx_usb->gadget.ep0->ep_list); - ep0_chg_stat(__func__, imx_usb, EP0_IDLE); - - /* basic endpoint records init */ - for (i = 0; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - - if (i) { - list_add_tail(&imx_ep->ep.ep_list, - &imx_usb->gadget.ep_list); - imx_ep->stopped = 1; - } else - imx_ep->stopped = 0; - - INIT_LIST_HEAD(&imx_ep->queue); - } -} - -static void udc_stop_activity(struct imx_udc_struct *imx_usb, - struct usb_gadget_driver *driver) -{ - struct imx_ep_struct *imx_ep; - int i; - - if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; - - /* prevent new request submissions, kill any outstanding requests */ - for (i = 1; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - imx_flush(imx_ep); - imx_ep->stopped = 1; - imx_ep_irq_disable(imx_ep); - nuke(imx_ep, -ESHUTDOWN); - } - - imx_usb->cfg = 0; - imx_usb->intf = 0; - imx_usb->alt = 0; - - if (driver) - driver->disconnect(&imx_usb->gadget); -} - -/******************************************************************************* - * Interrupt handlers - ******************************************************************************* - */ - -/* - * Called when timer expires. - * Timer is started when CFG_CHG is received. - */ -static void handle_config(unsigned long data) -{ - struct imx_udc_struct *imx_usb = (void *)data; - struct usb_ctrlrequest u; - int temp, cfg, intf, alt; - - local_irq_disable(); - - temp = __raw_readl(imx_usb->base + USB_STAT); - cfg = (temp & STAT_CFG) >> 5; - intf = (temp & STAT_INTF) >> 3; - alt = temp & STAT_ALTSET; - - D_REQ(imx_usb->dev, - "<%s> orig config C=%d, I=%d, A=%d / " - "req config C=%d, I=%d, A=%d\n", - __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt, - cfg, intf, alt); - - if (cfg == 1 || cfg == 2) { - - if (imx_usb->cfg != cfg) { - u.bRequest = USB_REQ_SET_CONFIGURATION; - u.bRequestType = USB_DIR_OUT | - USB_TYPE_STANDARD | - USB_RECIP_DEVICE; - u.wValue = cfg; - u.wIndex = 0; - u.wLength = 0; - imx_usb->cfg = cfg; - imx_usb->driver->setup(&imx_usb->gadget, &u); - - } - if (imx_usb->intf != intf || imx_usb->alt != alt) { - u.bRequest = USB_REQ_SET_INTERFACE; - u.bRequestType = USB_DIR_OUT | - USB_TYPE_STANDARD | - USB_RECIP_INTERFACE; - u.wValue = alt; - u.wIndex = intf; - u.wLength = 0; - imx_usb->intf = intf; - imx_usb->alt = alt; - imx_usb->driver->setup(&imx_usb->gadget, &u); - } - } - - imx_usb->set_config = 0; - - local_irq_enable(); -} - -static irqreturn_t imx_udc_irq(int irq, void *dev) -{ - struct imx_udc_struct *imx_usb = dev; - int intr = __raw_readl(imx_usb->base + USB_INTR); - int temp; - - if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START - | INTR_RESET_STOP | INTR_CFG_CHG)) { - dump_intr(__func__, intr, imx_usb->dev); - dump_usb_stat(__func__, imx_usb); - } - - if (!imx_usb->driver) - goto end_irq; - - if (intr & INTR_SOF) { - /* Copy from Freescale BSP. - We must enable SOF intr and set CMDOVER. - Datasheet don't specifiy this action, but it - is done in Freescale BSP, so just copy it. - */ - if (imx_usb->ep0state == EP0_IDLE) { - temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp | CTRL_CMDOVER, - imx_usb->base + USB_CTRL); - } - } - - if (intr & INTR_CFG_CHG) { - /* A workaround of serious IMX UDC bug. - Handling of CFG_CHG should be delayed for some time, because - IMX does not NACK the host when CFG_CHG interrupt is pending. - There is no time to handle current CFG_CHG - if next CFG_CHG or SETUP packed is send immediately. - We have to clear CFG_CHG, start the timer and - NACK the host by setting CTRL_CMDOVER - if it sends any SETUP packet. - When timer expires, handler is called to handle configuration - changes. While CFG_CHG is not handled (set_config=1), - we must NACK the host to every SETUP packed. - This delay prevents from going out of sync with host. - */ - __raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR); - imx_usb->set_config = 1; - mod_timer(&imx_usb->timer, jiffies + 5); - goto end_irq; - } - - if (intr & INTR_WAKEUP) { - if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN - && imx_usb->driver && imx_usb->driver->resume) - imx_usb->driver->resume(&imx_usb->gadget); - imx_usb->set_config = 0; - del_timer(&imx_usb->timer); - imx_usb->gadget.speed = USB_SPEED_FULL; - } - - if (intr & INTR_SUSPEND) { - if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN - && imx_usb->driver && imx_usb->driver->suspend) - imx_usb->driver->suspend(&imx_usb->gadget); - imx_usb->set_config = 0; - del_timer(&imx_usb->timer); - imx_usb->gadget.speed = USB_SPEED_UNKNOWN; - } - - if (intr & INTR_RESET_START) { - __raw_writel(intr, imx_usb->base + USB_INTR); - udc_stop_activity(imx_usb, imx_usb->driver); - imx_usb->set_config = 0; - del_timer(&imx_usb->timer); - imx_usb->gadget.speed = USB_SPEED_UNKNOWN; - } - - if (intr & INTR_RESET_STOP) - imx_usb->gadget.speed = USB_SPEED_FULL; - -end_irq: - __raw_writel(intr, imx_usb->base + USB_INTR); - return IRQ_HANDLED; -} - -static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev) -{ - struct imx_udc_struct *imx_usb = dev; - struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0]; - int intr = __raw_readl(imx_usb->base + USB_EP_INTR(0)); - - dump_ep_intr(__func__, 0, intr, imx_usb->dev); - - if (!imx_usb->driver) { - __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); - return IRQ_HANDLED; - } - - /* DEVREQ has highest priority */ - if (intr & (EPINTR_DEVREQ | EPINTR_MDEVREQ)) - handle_ep0_devreq(imx_usb); - /* Seem i.MX is missing EOF interrupt sometimes. - * Therefore we don't monitor EOF. - * We call handle_ep0() only if a request is queued for ep0. - */ - else if (!list_empty(&imx_ep->queue)) - handle_ep0(imx_ep); - - __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); - - return IRQ_HANDLED; -} - -#ifndef MX1_INT_USBD0 -#define MX1_INT_USBD0 MX1_USBD_INT0 -#endif - -static irqreturn_t imx_udc_bulk_irq(int irq, void *dev) -{ - struct imx_udc_struct *imx_usb = dev; - struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - MX1_INT_USBD0]; - int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); - - dump_ep_intr(__func__, irq - MX1_INT_USBD0, intr, imx_usb->dev); - - if (!imx_usb->driver) { - __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); - return IRQ_HANDLED; - } - - handle_ep(imx_ep); - - __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); - - return IRQ_HANDLED; -} - -irq_handler_t intr_handler(int i) -{ - switch (i) { - case 0: - return imx_udc_ctrl_irq; - case 1: - case 2: - case 3: - case 4: - case 5: - return imx_udc_bulk_irq; - default: - return imx_udc_irq; - } -} - -/******************************************************************************* - * Static defined IMX UDC structure - ******************************************************************************* - */ - -static int imx_udc_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver); -static int imx_udc_stop(struct usb_gadget *gadget, - struct usb_gadget_driver *driver); -static const struct usb_gadget_ops imx_udc_ops = { - .get_frame = imx_udc_get_frame, - .wakeup = imx_udc_wakeup, - .udc_start = imx_udc_start, - .udc_stop = imx_udc_stop, -}; - -static struct imx_udc_struct controller = { - .gadget = { - .ops = &imx_udc_ops, - .ep0 = &controller.imx_ep[0].ep, - .name = driver_name, - .dev = { - .init_name = "gadget", - }, - }, - - .imx_ep[0] = { - .ep = { - .name = ep0name, - .ops = &imx_ep_ops, - .maxpacket = 32, - }, - .imx_usb = &controller, - .fifosize = 32, - .bEndpointAddress = 0, - .bmAttributes = USB_ENDPOINT_XFER_CONTROL, - }, - .imx_ep[1] = { - .ep = { - .name = "ep1in-bulk", - .ops = &imx_ep_ops, - .maxpacket = 64, - }, - .imx_usb = &controller, - .fifosize = 64, - .bEndpointAddress = USB_DIR_IN | 1, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - }, - .imx_ep[2] = { - .ep = { - .name = "ep2out-bulk", - .ops = &imx_ep_ops, - .maxpacket = 64, - }, - .imx_usb = &controller, - .fifosize = 64, - .bEndpointAddress = USB_DIR_OUT | 2, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - }, - .imx_ep[3] = { - .ep = { - .name = "ep3out-bulk", - .ops = &imx_ep_ops, - .maxpacket = 32, - }, - .imx_usb = &controller, - .fifosize = 32, - .bEndpointAddress = USB_DIR_OUT | 3, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - }, - .imx_ep[4] = { - .ep = { - .name = "ep4in-int", - .ops = &imx_ep_ops, - .maxpacket = 32, - }, - .imx_usb = &controller, - .fifosize = 32, - .bEndpointAddress = USB_DIR_IN | 4, - .bmAttributes = USB_ENDPOINT_XFER_INT, - }, - .imx_ep[5] = { - .ep = { - .name = "ep5out-int", - .ops = &imx_ep_ops, - .maxpacket = 32, - }, - .imx_usb = &controller, - .fifosize = 32, - .bEndpointAddress = USB_DIR_OUT | 5, - .bmAttributes = USB_ENDPOINT_XFER_INT, - }, -}; - -/******************************************************************************* - * USB gadget driver functions - ******************************************************************************* - */ -static int imx_udc_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - struct imx_udc_struct *imx_usb; - - imx_usb = container_of(gadget, struct imx_udc_struct, gadget); - /* first hook up the driver ... */ - imx_usb->driver = driver; - - D_INI(imx_usb->dev, "<%s> registered gadget driver '%s'\n", - __func__, driver->driver.name); - - imx_udc_enable(imx_usb); - - return 0; -} - -static int imx_udc_stop(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - struct imx_udc_struct *imx_usb = container_of(gadget, - struct imx_udc_struct, gadget); - - udc_stop_activity(imx_usb, driver); - imx_udc_disable(imx_usb); - del_timer(&imx_usb->timer); - - imx_usb->driver = NULL; - - D_INI(imx_usb->dev, "<%s> unregistered gadget driver '%s'\n", - __func__, driver->driver.name); - - return 0; -} - -/******************************************************************************* - * Module functions - ******************************************************************************* - */ - -static int __init imx_udc_probe(struct platform_device *pdev) -{ - struct imx_udc_struct *imx_usb = &controller; - struct resource *res; - struct imxusb_platform_data *pdata; - struct clk *clk; - void __iomem *base; - int ret = 0; - int i; - resource_size_t res_size; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "can't get device resources\n"); - return -ENODEV; - } - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "driver needs platform data\n"); - return -ENODEV; - } - - res_size = resource_size(res); - if (!request_mem_region(res->start, res_size, res->name)) { - dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", - res_size, res->start); - return -ENOMEM; - } - - if (pdata->init) { - ret = pdata->init(&pdev->dev); - if (ret) - goto fail0; - } - - base = ioremap(res->start, res_size); - if (!base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -EIO; - goto fail1; - } - - clk = clk_get(NULL, "usbd_clk"); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err(&pdev->dev, "can't get USB clock\n"); - goto fail2; - } - clk_prepare_enable(clk); - - if (clk_get_rate(clk) != 48000000) { - D_INI(&pdev->dev, - "Bad USB clock (%d Hz), changing to 48000000 Hz\n", - (int)clk_get_rate(clk)); - if (clk_set_rate(clk, 48000000)) { - dev_err(&pdev->dev, - "Unable to set correct USB clock (48MHz)\n"); - ret = -EIO; - goto fail3; - } - } - - for (i = 0; i < IMX_USB_NB_EP + 1; i++) { - imx_usb->usbd_int[i] = platform_get_irq(pdev, i); - if (imx_usb->usbd_int[i] < 0) { - dev_err(&pdev->dev, "can't get irq number\n"); - ret = -ENODEV; - goto fail3; - } - } - - for (i = 0; i < IMX_USB_NB_EP + 1; i++) { - ret = request_irq(imx_usb->usbd_int[i], intr_handler(i), - 0, driver_name, imx_usb); - if (ret) { - dev_err(&pdev->dev, "can't get irq %i, err %d\n", - imx_usb->usbd_int[i], ret); - for (--i; i >= 0; i--) - free_irq(imx_usb->usbd_int[i], imx_usb); - goto fail3; - } - } - - imx_usb->res = res; - imx_usb->base = base; - imx_usb->clk = clk; - imx_usb->dev = &pdev->dev; - - platform_set_drvdata(pdev, imx_usb); - - usb_init_data(imx_usb); - imx_udc_init(imx_usb); - - init_timer(&imx_usb->timer); - imx_usb->timer.function = handle_config; - imx_usb->timer.data = (unsigned long)imx_usb; - - ret = usb_add_gadget_udc(&pdev->dev, &imx_usb->gadget); - if (ret) - goto fail4; - - return 0; -fail4: - for (i = 0; i < IMX_USB_NB_EP + 1; i++) - free_irq(imx_usb->usbd_int[i], imx_usb); -fail3: - clk_put(clk); - clk_disable_unprepare(clk); -fail2: - iounmap(base); -fail1: - if (pdata->exit) - pdata->exit(&pdev->dev); -fail0: - release_mem_region(res->start, res_size); - return ret; -} - -static int __exit imx_udc_remove(struct platform_device *pdev) -{ - struct imx_udc_struct *imx_usb = platform_get_drvdata(pdev); - struct imxusb_platform_data *pdata = pdev->dev.platform_data; - int i; - - usb_del_gadget_udc(&imx_usb->gadget); - imx_udc_disable(imx_usb); - del_timer(&imx_usb->timer); - - for (i = 0; i < IMX_USB_NB_EP + 1; i++) - free_irq(imx_usb->usbd_int[i], imx_usb); - - clk_put(imx_usb->clk); - clk_disable_unprepare(imx_usb->clk); - iounmap(imx_usb->base); - - release_mem_region(imx_usb->res->start, resource_size(imx_usb->res)); - - if (pdata->exit) - pdata->exit(&pdev->dev); - - return 0; -} - -/*----------------------------------------------------------------------------*/ - -#ifdef CONFIG_PM -#define imx_udc_suspend NULL -#define imx_udc_resume NULL -#else -#define imx_udc_suspend NULL -#define imx_udc_resume NULL -#endif - -/*----------------------------------------------------------------------------*/ - -static struct platform_driver udc_driver = { - .driver = { - .name = driver_name, - .owner = THIS_MODULE, - }, - .remove = __exit_p(imx_udc_remove), - .suspend = imx_udc_suspend, - .resume = imx_udc_resume, -}; - -module_platform_driver_probe(udc_driver, imx_udc_probe); - -MODULE_DESCRIPTION("IMX USB Device Controller driver"); -MODULE_AUTHOR("Darius Augulis "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx_udc"); diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h deleted file mode 100644 index d118fb777840..000000000000 --- a/drivers/usb/gadget/imx_udc.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2005 Mike Lee(eemike@gmail.com) - * - * This udc driver is now under testing and code is based on pxa2xx_udc.h - * Please use it with your own risk! - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __LINUX_USB_GADGET_IMX_H -#define __LINUX_USB_GADGET_IMX_H - -#include - -/* Helper macros */ -#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */ -#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0) -#define IMX_USB_NB_EP 6 - -/* Driver structures */ -struct imx_request { - struct usb_request req; - struct list_head queue; - unsigned int in_use; -}; - -enum ep0_state { - EP0_IDLE, - EP0_IN_DATA_PHASE, - EP0_OUT_DATA_PHASE, - EP0_CONFIG, - EP0_STALL, -}; - -struct imx_ep_struct { - struct usb_ep ep; - struct imx_udc_struct *imx_usb; - struct list_head queue; - unsigned char stopped; - unsigned char fifosize; - unsigned char bEndpointAddress; - unsigned char bmAttributes; -}; - -struct imx_udc_struct { - struct usb_gadget gadget; - struct usb_gadget_driver *driver; - struct device *dev; - struct imx_ep_struct imx_ep[IMX_USB_NB_EP]; - struct clk *clk; - struct timer_list timer; - enum ep0_state ep0state; - struct resource *res; - void __iomem *base; - unsigned char set_config; - int cfg, - intf, - alt, - usbd_int[7]; -}; - -/* USB registers */ -#define USB_FRAME (0x00) /* USB frame */ -#define USB_SPEC (0x04) /* USB Spec */ -#define USB_STAT (0x08) /* USB Status */ -#define USB_CTRL (0x0C) /* USB Control */ -#define USB_DADR (0x10) /* USB Desc RAM addr */ -#define USB_DDAT (0x14) /* USB Desc RAM/EP buffer data */ -#define USB_INTR (0x18) /* USB interrupt */ -#define USB_MASK (0x1C) /* USB Mask */ -#define USB_ENAB (0x24) /* USB Enable */ -#define USB_EP_STAT(x) (0x30 + (x*0x30)) /* USB status/control */ -#define USB_EP_INTR(x) (0x34 + (x*0x30)) /* USB interrupt */ -#define USB_EP_MASK(x) (0x38 + (x*0x30)) /* USB mask */ -#define USB_EP_FDAT(x) (0x3C + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FDAT0(x) (0x3C + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FDAT1(x) (0x3D + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FDAT2(x) (0x3E + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FDAT3(x) (0x3F + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FSTAT(x) (0x40 + (x*0x30)) /* USB FIFO status */ -#define USB_EP_FCTRL(x) (0x44 + (x*0x30)) /* USB FIFO control */ -#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last rd f. pointer */ -#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last wr f. pointer */ -#define USB_EP_FALRM(x) (0x50 + (x*0x30)) /* USB FIFO alarm */ -#define USB_EP_FRDP(x) (0x54 + (x*0x30)) /* USB FIFO read pointer */ -#define USB_EP_FWRP(x) (0x58 + (x*0x30)) /* USB FIFO write pointer */ -/* USB Control Register Bit Fields.*/ -#define CTRL_CMDOVER (1<<6) /* UDC status */ -#define CTRL_CMDERROR (1<<5) /* UDC status */ -#define CTRL_FE_ENA (1<<3) /* Enable Font End logic */ -#define CTRL_UDC_RST (1<<2) /* UDC reset */ -#define CTRL_AFE_ENA (1<<1) /* Analog Font end enable */ -#define CTRL_RESUME (1<<0) /* UDC resume */ -/* USB Status Register Bit Fields.*/ -#define STAT_RST (1<<8) -#define STAT_SUSP (1<<7) -#define STAT_CFG (3<<5) -#define STAT_INTF (3<<3) -#define STAT_ALTSET (7<<0) -/* USB Interrupt Status/Mask Registers Bit fields */ -#define INTR_WAKEUP (1<<31) /* Wake up Interrupt */ -#define INTR_MSOF (1<<7) /* Missed Start of Frame */ -#define INTR_SOF (1<<6) /* Start of Frame */ -#define INTR_RESET_STOP (1<<5) /* Reset Signaling stop */ -#define INTR_RESET_START (1<<4) /* Reset Signaling start */ -#define INTR_RESUME (1<<3) /* Suspend to resume */ -#define INTR_SUSPEND (1<<2) /* Active to suspend */ -#define INTR_FRAME_MATCH (1<<1) /* Frame matched */ -#define INTR_CFG_CHG (1<<0) /* Configuration change occurred */ -/* USB Enable Register Bit Fields.*/ -#define ENAB_RST (1<<31) /* Reset USB modules */ -#define ENAB_ENAB (1<<30) /* Enable USB modules*/ -#define ENAB_SUSPEND (1<<29) /* Suspend USB modules */ -#define ENAB_ENDIAN (1<<28) /* Endian of USB modules */ -#define ENAB_PWRMD (1<<0) /* Power mode of USB modules */ -/* USB Descriptor Ram Address Register bit fields */ -#define DADR_CFG (1<<31) /* Configuration */ -#define DADR_BSY (1<<30) /* Busy status */ -#define DADR_DADR (0x1FF) /* Descriptor Ram Address */ -/* USB Descriptor RAM/Endpoint Buffer Data Register bit fields */ -#define DDAT_DDAT (0xFF) /* Descriptor Endpoint Buffer */ -/* USB Endpoint Status Register bit fields */ -#define EPSTAT_BCOUNT (0x7F<<16) /* Endpoint FIFO byte count */ -#define EPSTAT_SIP (1<<8) /* Endpoint setup in progress */ -#define EPSTAT_DIR (1<<7) /* Endpoint transfer direction */ -#define EPSTAT_MAX (3<<5) /* Endpoint Max packet size */ -#define EPSTAT_TYP (3<<3) /* Endpoint type */ -#define EPSTAT_ZLPS (1<<2) /* Send zero length packet */ -#define EPSTAT_FLUSH (1<<1) /* Endpoint FIFO Flush */ -#define EPSTAT_STALL (1<<0) /* Force stall */ -/* USB Endpoint FIFO Status Register bit fields */ -#define FSTAT_FRAME_STAT (0xF<<24) /* Frame status bit [0-3] */ -#define FSTAT_ERR (1<<22) /* FIFO error */ -#define FSTAT_UF (1<<21) /* FIFO underflow */ -#define FSTAT_OF (1<<20) /* FIFO overflow */ -#define FSTAT_FR (1<<19) /* FIFO frame ready */ -#define FSTAT_FULL (1<<18) /* FIFO full */ -#define FSTAT_ALRM (1<<17) /* FIFO alarm */ -#define FSTAT_EMPTY (1<<16) /* FIFO empty */ -/* USB Endpoint FIFO Control Register bit fields */ -#define FCTRL_WFR (1<<29) /* Write frame end */ -/* USB Endpoint Interrupt Status Regsiter bit fields */ -#define EPINTR_FIFO_FULL (1<<8) /* fifo full */ -#define EPINTR_FIFO_EMPTY (1<<7) /* fifo empty */ -#define EPINTR_FIFO_ERROR (1<<6) /* fifo error */ -#define EPINTR_FIFO_HIGH (1<<5) /* fifo high */ -#define EPINTR_FIFO_LOW (1<<4) /* fifo low */ -#define EPINTR_MDEVREQ (1<<3) /* multi Device request */ -#define EPINTR_EOT (1<<2) /* fifo end of transfer */ -#define EPINTR_DEVREQ (1<<1) /* Device request */ -#define EPINTR_EOF (1<<0) /* fifo end of frame */ - -/* Debug macros */ -#ifdef DEBUG - -/* #define DEBUG_REQ */ -/* #define DEBUG_TRX */ -/* #define DEBUG_INIT */ -/* #define DEBUG_EP0 */ -/* #define DEBUG_EPX */ -/* #define DEBUG_IRQ */ -/* #define DEBUG_EPIRQ */ -/* #define DEBUG_DUMP */ -/* #define DEBUG_ERR */ - -#ifdef DEBUG_REQ - #define D_REQ(dev, args...) dev_dbg(dev, ## args) -#else - #define D_REQ(dev, args...) do {} while (0) -#endif /* DEBUG_REQ */ - -#ifdef DEBUG_TRX - #define D_TRX(dev, args...) dev_dbg(dev, ## args) -#else - #define D_TRX(dev, args...) do {} while (0) -#endif /* DEBUG_TRX */ - -#ifdef DEBUG_INIT - #define D_INI(dev, args...) dev_dbg(dev, ## args) -#else - #define D_INI(dev, args...) do {} while (0) -#endif /* DEBUG_INIT */ - -#ifdef DEBUG_EP0 - static const char *state_name[] = { - "EP0_IDLE", - "EP0_IN_DATA_PHASE", - "EP0_OUT_DATA_PHASE", - "EP0_CONFIG", - "EP0_STALL" - }; - #define D_EP0(dev, args...) dev_dbg(dev, ## args) -#else - #define D_EP0(dev, args...) do {} while (0) -#endif /* DEBUG_EP0 */ - -#ifdef DEBUG_EPX - #define D_EPX(dev, args...) dev_dbg(dev, ## args) -#else - #define D_EPX(dev, args...) do {} while (0) -#endif /* DEBUG_EP0 */ - -#ifdef DEBUG_IRQ - static void dump_intr(const char *label, int irqreg, struct device *dev) - { - dev_dbg(dev, "<%s> USB_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, - (irqreg & INTR_WAKEUP) ? " wake" : "", - (irqreg & INTR_MSOF) ? " msof" : "", - (irqreg & INTR_SOF) ? " sof" : "", - (irqreg & INTR_RESUME) ? " resume" : "", - (irqreg & INTR_SUSPEND) ? " suspend" : "", - (irqreg & INTR_RESET_STOP) ? " noreset" : "", - (irqreg & INTR_RESET_START) ? " reset" : "", - (irqreg & INTR_FRAME_MATCH) ? " fmatch" : "", - (irqreg & INTR_CFG_CHG) ? " config" : ""); - } -#else - #define dump_intr(x, y, z) do {} while (0) -#endif /* DEBUG_IRQ */ - -#ifdef DEBUG_EPIRQ - static void dump_ep_intr(const char *label, int nr, int irqreg, - struct device *dev) - { - dev_dbg(dev, "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, nr, - (irqreg & EPINTR_FIFO_FULL) ? " full" : "", - (irqreg & EPINTR_FIFO_EMPTY) ? " fempty" : "", - (irqreg & EPINTR_FIFO_ERROR) ? " ferr" : "", - (irqreg & EPINTR_FIFO_HIGH) ? " fhigh" : "", - (irqreg & EPINTR_FIFO_LOW) ? " flow" : "", - (irqreg & EPINTR_MDEVREQ) ? " mreq" : "", - (irqreg & EPINTR_EOF) ? " eof" : "", - (irqreg & EPINTR_DEVREQ) ? " devreq" : "", - (irqreg & EPINTR_EOT) ? " eot" : ""); - } -#else - #define dump_ep_intr(x, y, z, i) do {} while (0) -#endif /* DEBUG_IRQ */ - -#ifdef DEBUG_DUMP - static void dump_usb_stat(const char *label, - struct imx_udc_struct *imx_usb) - { - int temp = __raw_readl(imx_usb->base + USB_STAT); - - dev_dbg(imx_usb->dev, - "<%s> USB_STAT=[%s%s CFG=%d, INTF=%d, ALTR=%d]\n", label, - (temp & STAT_RST) ? " reset" : "", - (temp & STAT_SUSP) ? " suspend" : "", - (temp & STAT_CFG) >> 5, - (temp & STAT_INTF) >> 3, - (temp & STAT_ALTSET)); - } - - static void dump_ep_stat(const char *label, - struct imx_ep_struct *imx_ep) - { - int temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_INTR(EP_NO(imx_ep))); - - dev_dbg(imx_ep->imx_usb->dev, - "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", - label, EP_NO(imx_ep), - (temp & EPINTR_FIFO_FULL) ? " full" : "", - (temp & EPINTR_FIFO_EMPTY) ? " fempty" : "", - (temp & EPINTR_FIFO_ERROR) ? " ferr" : "", - (temp & EPINTR_FIFO_HIGH) ? " fhigh" : "", - (temp & EPINTR_FIFO_LOW) ? " flow" : "", - (temp & EPINTR_MDEVREQ) ? " mreq" : "", - (temp & EPINTR_EOF) ? " eof" : "", - (temp & EPINTR_DEVREQ) ? " devreq" : "", - (temp & EPINTR_EOT) ? " eot" : ""); - - temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_STAT(EP_NO(imx_ep))); - - dev_dbg(imx_ep->imx_usb->dev, - "<%s> EP%d_STAT=[%s%s bcount=%d]\n", - label, EP_NO(imx_ep), - (temp & EPSTAT_SIP) ? " sip" : "", - (temp & EPSTAT_STALL) ? " stall" : "", - (temp & EPSTAT_BCOUNT) >> 16); - - temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_FSTAT(EP_NO(imx_ep))); - - dev_dbg(imx_ep->imx_usb->dev, - "<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n", - label, EP_NO(imx_ep), - (temp & FSTAT_ERR) ? " ferr" : "", - (temp & FSTAT_UF) ? " funder" : "", - (temp & FSTAT_OF) ? " fover" : "", - (temp & FSTAT_FR) ? " fready" : "", - (temp & FSTAT_FULL) ? " ffull" : "", - (temp & FSTAT_ALRM) ? " falarm" : "", - (temp & FSTAT_EMPTY) ? " fempty" : ""); - } - - static void dump_req(const char *label, struct imx_ep_struct *imx_ep, - struct usb_request *req) - { - int i; - - if (!req || !req->buf) { - dev_dbg(imx_ep->imx_usb->dev, - "<%s> req or req buf is free\n", label); - return; - } - - if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state - == EP0_IN_DATA_PHASE) - || (EP_NO(imx_ep) && EP_DIR(imx_ep))) { - - dev_dbg(imx_ep->imx_usb->dev, - "<%s> request dump <", label); - for (i = 0; i < req->length; i++) - printk("%02x-", *((u8 *)req->buf + i)); - printk(">\n"); - } - } - -#else - #define dump_ep_stat(x, y) do {} while (0) - #define dump_usb_stat(x, y) do {} while (0) - #define dump_req(x, y, z) do {} while (0) -#endif /* DEBUG_DUMP */ - -#ifdef DEBUG_ERR - #define D_ERR(dev, args...) dev_dbg(dev, ## args) -#else - #define D_ERR(dev, args...) do {} while (0) -#endif - -#else - #define D_REQ(dev, args...) do {} while (0) - #define D_TRX(dev, args...) do {} while (0) - #define D_INI(dev, args...) do {} while (0) - #define D_EP0(dev, args...) do {} while (0) - #define D_EPX(dev, args...) do {} while (0) - #define dump_ep_intr(x, y, z, i) do {} while (0) - #define dump_intr(x, y, z) do {} while (0) - #define dump_ep_stat(x, y) do {} while (0) - #define dump_usb_stat(x, y) do {} while (0) - #define dump_req(x, y, z) do {} while (0) - #define D_ERR(dev, args...) do {} while (0) -#endif /* DEBUG */ - -#endif /* __LINUX_USB_GADGET_IMX_H */ -- cgit v1.2.3-58-ga151 From b0d7ffd44ba9cd2dfbf299674418193a5f9ed21a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 27 Jun 2013 10:00:18 +0300 Subject: usb: dwc3: gadget: don't request IRQs in atomic We cannot request an IRQ with spinlocks held as that would trigger a sleeping inside spinlock warning. Cc: # v3.10 Reported-by: Stephen Boyd Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f77083fedc68..14d28d6184f6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1508,6 +1508,15 @@ static int dwc3_gadget_start(struct usb_gadget *g, int irq; u32 reg; + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, + IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + goto err0; + } + spin_lock_irqsave(&dwc->lock, flags); if (dwc->gadget_driver) { @@ -1515,7 +1524,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc->gadget.name, dwc->gadget_driver->driver.name); ret = -EBUSY; - goto err0; + goto err1; } dwc->gadget_driver = driver; @@ -1551,42 +1560,38 @@ static int dwc3_gadget_start(struct usb_gadget *g, ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - goto err0; + goto err2; } dep = dwc->eps[1]; ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - goto err1; + goto err3; } /* begin to receive SETUP packets */ dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, - IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc); - if (ret) { - dev_err(dwc->dev, "failed to request irq #%d --> %d\n", - irq, ret); - goto err1; - } - dwc3_gadget_enable_irq(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return 0; -err1: +err3: __dwc3_gadget_ep_disable(dwc->eps[0]); -err0: +err2: dwc->gadget_driver = NULL; + +err1: spin_unlock_irqrestore(&dwc->lock, flags); + free_irq(irq, dwc); + +err0: return ret; } @@ -1600,9 +1605,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g, spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_disable_irq(dwc); - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - free_irq(irq, dwc); - __dwc3_gadget_ep_disable(dwc->eps[0]); __dwc3_gadget_ep_disable(dwc->eps[1]); @@ -1610,6 +1612,9 @@ static int dwc3_gadget_stop(struct usb_gadget *g, spin_unlock_irqrestore(&dwc->lock, flags); + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + free_irq(irq, dwc); + return 0; } -- cgit v1.2.3-58-ga151 From 7bc5a6ba369217e0137833f5955cf0b0f08b0712 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Thu, 27 Jun 2013 01:08:10 +0800 Subject: usb: dwc3: clean up redundant parameter comment @list is not as a parameter of dwc3_event_buffer, so remove it in comments. Signed-off-by: Huang Rui Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 27dad993b007..00a72e37397d 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -367,7 +367,6 @@ struct dwc3_trb; /** * struct dwc3_event_buffer - Software event buffer representation - * @list: a list of event buffers * @buf: _THE_ buffer * @length: size of this buffer * @lpos: event offset -- cgit v1.2.3-58-ga151 From 5945f789c864d8676b297880cc6961fea4511a7a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sun, 30 Jun 2013 14:15:11 +0300 Subject: usb: dwc3: switch to GPL v2 only This is a Linux-only driver which makes use of GPL-only symbols. It makes no sense to maintain Dual BSD/GPL licensing for this driver. Considering that the amount of work to use this driver in any different operating system would likely be as large as developing the driver from scratch and considering that we depend on GPL-only symbols, we will switch over to a GPL v2-only license. Cc: Anton Tikhomirov Acked-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 37 ++++++++++--------------------------- drivers/usb/dwc3/core.h | 34 +++++++--------------------------- drivers/usb/dwc3/debug.h | 34 +++++++--------------------------- drivers/usb/dwc3/debugfs.c | 34 +++++++--------------------------- drivers/usb/dwc3/dwc3-exynos.c | 14 +++++++++----- drivers/usb/dwc3/dwc3-omap.c | 36 ++++++++---------------------------- drivers/usb/dwc3/dwc3-pci.c | 36 ++++++++---------------------------- drivers/usb/dwc3/ep0.c | 34 +++++++--------------------------- drivers/usb/dwc3/gadget.c | 34 +++++++--------------------------- drivers/usb/dwc3/gadget.h | 34 +++++++--------------------------- drivers/usb/dwc3/host.c | 34 +++++++--------------------------- drivers/usb/dwc3/io.h | 34 +++++++--------------------------- 12 files changed, 91 insertions(+), 304 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 358375e0b291..f69e45a50039 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -6,34 +6,17 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ #include @@ -775,5 +758,5 @@ module_platform_driver(dwc3_driver); MODULE_ALIAS("platform:dwc3"); MODULE_AUTHOR("Felipe Balbi "); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 00a72e37397d..22ada27ad3ff 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -6,34 +6,14 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #ifndef __DRIVERS_USB_DWC3_CORE_H diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 5894ee8222af..fceb39dc4bba 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -6,34 +6,14 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include "core.h" diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 9e9f122162f2..9ac37fe1b6a7 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -6,34 +6,14 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 8ce9d7fd6cfc..9a8a5e1f394f 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -6,10 +6,14 @@ * * Author: Anton Tikhomirov * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include @@ -228,5 +232,5 @@ module_platform_driver(dwc3_exynos_driver); MODULE_ALIAS("platform:exynos-dwc3"); MODULE_AUTHOR("Anton Tikhomirov "); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer"); diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 077f110bd746..cf209086f3e8 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -6,34 +6,14 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include @@ -610,5 +590,5 @@ module_platform_driver(dwc3_omap_driver); MODULE_ALIAS("platform:omap-dwc3"); MODULE_AUTHOR("Felipe Balbi "); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer"); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index ed07ec04a962..5d746e5d6a0a 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -6,34 +6,14 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include @@ -257,7 +237,7 @@ static struct pci_driver dwc3_pci_driver = { }; MODULE_AUTHOR("Felipe Balbi "); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer"); module_pci_driver(dwc3_pci_driver); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 5acbb948b704..e9d099cee636 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -6,34 +6,14 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 14d28d6184f6..033419be26d4 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -6,34 +6,14 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 99e6d7248820..febe1aa7b714 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -6,34 +6,14 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #ifndef __DRIVERS_USB_DWC3_GADGET_H diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 0fa1846eda4c..32db328cc769 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -5,34 +5,14 @@ * * Authors: Felipe Balbi , * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index a50f76b9d19b..d94441c14d8c 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -6,34 +6,14 @@ * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #ifndef __DRIVERS_USB_DWC3_IO_H -- cgit v1.2.3-58-ga151 From 1494a1f62bf7cf57345e9282c8189fe2a21fab64 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sun, 30 Jun 2013 13:56:45 +0300 Subject: usb: common: introduce of_usb_get_maximum_speed() this helper will be used for controllers which want to work at a lower speed even though they support higher USB transfer rates. One such case is Texas Instruments' AM437x SoC where it uses a USB3 controller without a USB3 PHY, rendering the controller USB2-only. Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/generic.txt | 24 +++++++++++ drivers/usb/usb-common.c | 49 ++++++++++++++++++----- include/linux/usb/of.h | 8 ++++ 3 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 Documentation/devicetree/bindings/usb/generic.txt (limited to 'drivers/usb') diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt new file mode 100644 index 000000000000..477d5bb5e51c --- /dev/null +++ b/Documentation/devicetree/bindings/usb/generic.txt @@ -0,0 +1,24 @@ +Generic USB Properties + +Optional properties: + - maximum-speed: tells USB controllers we want to work up to a certain + speed. Valid arguments are "super-speed", "high-speed", + "full-speed" and "low-speed". In case this isn't passed + via DT, USB controllers should default to their maximum + HW capability. + - dr_mode: tells Dual-Role USB controllers that we want to work on a + particular mode. Valid arguments are "host", + "peripheral" and "otg". In case this attribute isn't + passed via DT, USB DRD controllers should default to + OTG. + +This is an attribute to a USB controller such as: + +dwc3@4a030000 { + compatible = "synopsys,dwc3"; + reg = <0x4a030000 0xcfff>; + interrupts = <0 92 4> + usb-phy = <&usb2_phy>, <&usb3,phy>; + maximum-speed = "super-speed"; + dr_mode = "otg"; +}; diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c index 675384dabfe9..d771870a819e 100644 --- a/drivers/usb/usb-common.c +++ b/drivers/usb/usb-common.c @@ -43,20 +43,20 @@ const char *usb_otg_state_string(enum usb_otg_state state) } EXPORT_SYMBOL_GPL(usb_otg_state_string); +static const char *const speed_names[] = { + [USB_SPEED_UNKNOWN] = "UNKNOWN", + [USB_SPEED_LOW] = "low-speed", + [USB_SPEED_FULL] = "full-speed", + [USB_SPEED_HIGH] = "high-speed", + [USB_SPEED_WIRELESS] = "wireless", + [USB_SPEED_SUPER] = "super-speed", +}; + const char *usb_speed_string(enum usb_device_speed speed) { - static const char *const names[] = { - [USB_SPEED_UNKNOWN] = "UNKNOWN", - [USB_SPEED_LOW] = "low-speed", - [USB_SPEED_FULL] = "full-speed", - [USB_SPEED_HIGH] = "high-speed", - [USB_SPEED_WIRELESS] = "wireless", - [USB_SPEED_SUPER] = "super-speed", - }; - - if (speed < 0 || speed >= ARRAY_SIZE(names)) + if (speed < 0 || speed >= ARRAY_SIZE(speed_names)) speed = USB_SPEED_UNKNOWN; - return names[speed]; + return speed_names[speed]; } EXPORT_SYMBOL_GPL(usb_speed_string); @@ -112,6 +112,33 @@ enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np) return USB_DR_MODE_UNKNOWN; } EXPORT_SYMBOL_GPL(of_usb_get_dr_mode); + +/** + * of_usb_get_maximum_speed - Get maximum requested speed for a given USB + * controller. + * @np: Pointer to the given device_node + * + * The function gets the maximum speed string from property "maximum-speed", + * and returns the corresponding enum usb_device_speed. + */ +enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np) +{ + const char *maximum_speed; + int err; + int i; + + err = of_property_read_string(np, "maximum-speed", &maximum_speed); + if (err < 0) + return USB_SPEED_UNKNOWN; + + for (i = 0; i < ARRAY_SIZE(speed_names); i++) + if (strcmp(maximum_speed, speed_names[i]) == 0) + return i; + + return USB_SPEED_UNKNOWN; +} +EXPORT_SYMBOL_GPL(of_usb_get_maximum_speed); + #endif MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index a0ef405368b8..5a7cb9ebf0ef 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -7,16 +7,24 @@ #ifndef __LINUX_USB_OF_H #define __LINUX_USB_OF_H +#include #include #include #if IS_ENABLED(CONFIG_OF) enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np); +enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np); #else static inline enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np) { return USB_DR_MODE_UNKNOWN; } + +static inline enum usb_device_speed +of_usb_get_maximum_speed(struct device_node *np) +{ + return USB_SPEED_UNKNOWN; +} #endif #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_PHY) -- cgit v1.2.3-58-ga151 From 6462cbd54d2fa649b27633267d8ddf835705e47a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sun, 30 Jun 2013 14:19:33 +0300 Subject: usb: dwc3: let non-DT platforms pass tx-fifo-resize flag; in case we're not in a DT boot, we should still be able to tell the driver how to behave. In order to be able to pass flags to the driver, we introduce platform_data structure which the core driver should use. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 8 ++++++-- drivers/usb/dwc3/platform_data.h | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/dwc3/platform_data.h (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f69e45a50039..cb1eb37c6d6c 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -37,6 +37,7 @@ #include #include +#include "platform_data.h" #include "core.h" #include "gadget.h" #include "io.h" @@ -350,6 +351,7 @@ static void dwc3_core_exit(struct dwc3 *dwc) static int dwc3_probe(struct platform_device *pdev) { + struct dwc3_platform_data *pdata = pdev->dev.platform_data; struct device_node *node = pdev->dev.of_node; struct resource *res; struct dwc3 *dwc; @@ -412,9 +414,13 @@ static int dwc3_probe(struct platform_device *pdev) if (node) { dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1); + + dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); } else { dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); + + dwc->needs_fifo_resize = pdata->tx_fifo_resize; } if (IS_ERR(dwc->usb2_phy)) { @@ -472,8 +478,6 @@ static int dwc3_probe(struct platform_device *pdev) else dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; - dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); - pm_runtime_enable(dev); pm_runtime_get_sync(dev); pm_runtime_forbid(dev); diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h new file mode 100644 index 000000000000..038516489539 --- /dev/null +++ b/drivers/usb/dwc3/platform_data.h @@ -0,0 +1,22 @@ +/** + * platform_data.h - USB DWC3 Platform Data Support + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * Author: Felipe Balbi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +struct dwc3_platform_data { + bool tx_fifo_resize; +}; -- cgit v1.2.3-58-ga151 From f7e846f0956917888b28b52726ee8779a39d84b6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sun, 30 Jun 2013 14:29:51 +0300 Subject: usb: dwc3: make maximum-speed a per-instance attribute in order to allow different instances of the core work in different maximum speeds, we will move the maximum_speed module_parameter to both DeviceTree (making use the new maximum-speed DT property) and platform_data. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 24 +++++++++--------------- drivers/usb/dwc3/gadget.c | 21 ++++++++++++++++++--- drivers/usb/dwc3/platform_data.h | 3 +++ 3 files changed, 30 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index cb1eb37c6d6c..0686acff1e22 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "platform_data.h" #include "core.h" @@ -44,10 +45,6 @@ #include "debug.h" -static char *maximum_speed = "super"; -module_param(maximum_speed, charp, 0); -MODULE_PARM_DESC(maximum_speed, "Maximum supported speed."); - /* -------------------------------------------------------------------------- */ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) @@ -412,17 +409,25 @@ static int dwc3_probe(struct platform_device *pdev) } if (node) { + dwc->maximum_speed = of_usb_get_maximum_speed(node); + dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1); dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); } else { + dwc->maximum_speed = pdata->maximum_speed; + dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); dwc->needs_fifo_resize = pdata->tx_fifo_resize; } + /* default to superspeed if no maximum_speed passed */ + if (dwc->maximum_speed == USB_SPEED_UNKNOWN) + dwc->maximum_speed = USB_SPEED_SUPER; + if (IS_ERR(dwc->usb2_phy)) { ret = PTR_ERR(dwc->usb2_phy); @@ -467,17 +472,6 @@ static int dwc3_probe(struct platform_device *pdev) dev->dma_parms = dev->parent->dma_parms; dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); - if (!strncmp("super", maximum_speed, 5)) - dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; - else if (!strncmp("high", maximum_speed, 4)) - dwc->maximum_speed = DWC3_DCFG_HIGHSPEED; - else if (!strncmp("full", maximum_speed, 4)) - dwc->maximum_speed = DWC3_DCFG_FULLSPEED1; - else if (!strncmp("low", maximum_speed, 3)) - dwc->maximum_speed = DWC3_DCFG_LOWSPEED; - else - dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; - pm_runtime_enable(dev); pm_runtime_get_sync(dev); pm_runtime_forbid(dev); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 033419be26d4..4ca5706f3a86 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1525,10 +1525,25 @@ static int dwc3_gadget_start(struct usb_gadget *g, * STAR#9000525659: Clock Domain Crossing on DCTL in * USB 2.0 Mode */ - if (dwc->revision < DWC3_REVISION_220A) + if (dwc->revision < DWC3_REVISION_220A) { reg |= DWC3_DCFG_SUPERSPEED; - else - reg |= dwc->maximum_speed; + } else { + switch (dwc->maximum_speed) { + case USB_SPEED_LOW: + reg |= DWC3_DSTS_LOWSPEED; + break; + case USB_SPEED_FULL: + reg |= DWC3_DSTS_FULLSPEED1; + break; + case USB_SPEED_HIGH: + reg |= DWC3_DSTS_HIGHSPEED; + break; + case USB_SPEED_SUPER: /* FALLTHROUGH */ + case USB_SPEED_UNKNOWN: /* FALTHROUGH */ + default: + reg |= DWC3_DSTS_SUPERSPEED; + } + } dwc3_writel(dwc->regs, DWC3_DCFG, reg); dwc->start_config_issued = false; diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index 038516489539..16ffe1912523 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -17,6 +17,9 @@ * along with this program. If not, see . */ +#include + struct dwc3_platform_data { + enum usb_device_speed maximum_speed; bool tx_fifo_resize; }; -- cgit v1.2.3-58-ga151 From 22a5aa170c5231e64e1598a55dee92ea9a26183e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 2 Jul 2013 21:20:24 +0300 Subject: usb: dwc3: core: switch to snps,dwc3 all other drivers using Synopsys IPs with DT have a compatible of snps,$driver, in order to add consistency, we are switching over to snps,dwc3 but keeping synopsys,dwc3 in the core driver to maintain backwards compatibility. New DTS bindings should NOT use synopsys,dwc3. Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/dwc3.txt | 4 ++-- arch/arm/boot/dts/omap5.dtsi | 2 +- drivers/usb/dwc3/core.c | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 7a95c651ceb3..3a64e20f6c32 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -3,7 +3,7 @@ synopsys DWC3 CORE DWC3- USB3 CONTROLLER Required properties: - - compatible: must be "synopsys,dwc3" + - compatible: must be "snps,dwc3" - reg : Address and length of the register set for the device - interrupts: Interrupts used by the dwc3 controller. - usb-phy : array of phandle for the PHY device @@ -14,7 +14,7 @@ Optional properties: This is usually a subnode to DWC3 glue to which it is connected. dwc3@4a030000 { - compatible = "synopsys,dwc3"; + compatible = "snps,dwc3"; reg = <0x4a030000 0xcfff>; interrupts = <0 92 4> usb-phy = <&usb2_phy>, <&usb3,phy>; diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi index e643620417a9..07be2cd7b318 100644 --- a/arch/arm/boot/dts/omap5.dtsi +++ b/arch/arm/boot/dts/omap5.dtsi @@ -644,7 +644,7 @@ utmi-mode = <2>; ranges; dwc3@4a030000 { - compatible = "synopsys,dwc3"; + compatible = "snps,dwc3"; reg = <0x4a030000 0x1000>; interrupts = ; usb-phy = <&usb2_phy>, <&usb3_phy>; diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 0686acff1e22..2f5632730ffb 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -734,6 +734,9 @@ static const struct dev_pm_ops dwc3_dev_pm_ops = { #ifdef CONFIG_OF static const struct of_device_id of_dwc3_match[] = { + { + .compatible = "snps,dwc3" + }, { .compatible = "synopsys,dwc3" }, -- cgit v1.2.3-58-ga151 From c50f056c360673ef196b49bfa161d841f0fbe92e Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 25 Jun 2013 17:38:23 +0200 Subject: usb: gadget: s3c-hsotg: Allow driver instantiation using device tree This patch adds OF match table to the driver to allow instantiating it using device tree. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/samsung-hsotg.txt | 40 ++++++++++++++++++++++ drivers/usb/gadget/s3c-hsotg.c | 10 ++++++ 2 files changed, 50 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/samsung-hsotg.txt (limited to 'drivers/usb') diff --git a/Documentation/devicetree/bindings/usb/samsung-hsotg.txt b/Documentation/devicetree/bindings/usb/samsung-hsotg.txt new file mode 100644 index 000000000000..b83d428a265e --- /dev/null +++ b/Documentation/devicetree/bindings/usb/samsung-hsotg.txt @@ -0,0 +1,40 @@ +Samsung High Speed USB OTG controller +----------------------------- + +The Samsung HSOTG IP can be found on Samsung SoCs, from S3C6400 onwards. +It gives functionality of OTG-compliant USB 2.0 host and device with +support for USB 2.0 high-speed (480Mbps) and full-speed (12 Mbps) +operation. + +Currently only device mode is supported. + +Binding details +----- + +Required properties: +- compatible: "samsung,s3c6400-hsotg" should be used for all currently + supported SoC, +- interrupt-parent: phandle for the interrupt controller to which the + interrupt signal of the HSOTG block is routed, +- interrupts: specifier of interrupt signal of interrupt controller, + according to bindings of interrupt controller, +- clocks: contains an array of clock specifiers: + - first entry: OTG clock +- clock-names: contains array of clock names: + - first entry: must be "otg" +- vusb_d-supply: phandle to voltage regulator of digital section, +- vusb_a-supply: phandle to voltage regulator of analog section. + +Example +----- + + hsotg@12480000 { + compatible = "samsung,s3c6400-hsotg"; + reg = <0x12480000 0x20000>; + interrupts = <0 71 0>; + clocks = <&clock 305>; + clock-names = "otg"; + vusb_d-supply = <&vusb_reg>; + vusb_a-supply = <&vusbdac_reg>; + }; + diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index af22f24046b2..616ed51f8585 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -3648,10 +3649,19 @@ static int s3c_hsotg_remove(struct platform_device *pdev) #define s3c_hsotg_resume NULL #endif +#ifdef CONFIG_OF +static const struct of_device_id s3c_hsotg_of_ids[] = { + { .compatible = "samsung,s3c6400-hsotg", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s3c_hsotg_of_ids); +#endif + static struct platform_driver s3c_hsotg_driver = { .driver = { .name = "s3c-hsotg", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(s3c_hsotg_of_ids), }, .probe = s3c_hsotg_probe, .remove = s3c_hsotg_remove, -- cgit v1.2.3-58-ga151 From 24dc3538bcfb353741e653fab550c4bf0a8dc07d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Jul 2013 21:34:58 -0700 Subject: usb: musb: get rid of unused proc_dir_entry The musb driver no longer uses procfs, so get rid of the proc_dir_entry variable in struct musb. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 7d341c387eab..65f3917b4fc5 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -83,11 +83,6 @@ enum { MUSB_PORT_MODE_DUAL_ROLE, }; -#ifdef CONFIG_PROC_FS -#include -#define MUSB_CONFIG_PROC_FS -#endif - /****************************** CONSTANTS ********************************/ #ifndef MUSB_C_NUM_EPS @@ -425,9 +420,6 @@ struct musb { struct musb_hdrc_config *config; -#ifdef MUSB_CONFIG_PROC_FS - struct proc_dir_entry *proc_entry; -#endif int xceiv_old_state; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root; -- cgit v1.2.3-58-ga151 From 51e563e3c11492748d11d3846b6d325426427a95 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 3 Jul 2013 16:34:13 -0300 Subject: usb: phy: phy-mxs-usb: Check the return value from stmp_reset_block() stmp_reset_block() may fail, so let's check its return value and propagate it in the case of error. Acked-by: Shawn Guo Signed-off-by: Fabio Estevam Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index bd601c537c8d..fdd33b44dbd3 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -41,11 +41,14 @@ struct mxs_phy { #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) -static void mxs_phy_hw_init(struct mxs_phy *mxs_phy) +static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) { + int ret; void __iomem *base = mxs_phy->phy.io_priv; - stmp_reset_block(base + HW_USBPHY_CTRL); + ret = stmp_reset_block(base + HW_USBPHY_CTRL); + if (ret) + return ret; /* Power up the PHY */ writel(0, base + HW_USBPHY_PWD); @@ -54,6 +57,8 @@ static void mxs_phy_hw_init(struct mxs_phy *mxs_phy) writel(BM_USBPHY_CTRL_ENUTMILEVEL2 | BM_USBPHY_CTRL_ENUTMILEVEL3, base + HW_USBPHY_CTRL_SET); + + return 0; } static int mxs_phy_init(struct usb_phy *phy) @@ -61,9 +66,7 @@ static int mxs_phy_init(struct usb_phy *phy) struct mxs_phy *mxs_phy = to_mxs_phy(phy); clk_prepare_enable(mxs_phy->clk); - mxs_phy_hw_init(mxs_phy); - - return 0; + return mxs_phy_hw_init(mxs_phy); } static void mxs_phy_shutdown(struct usb_phy *phy) -- cgit v1.2.3-58-ga151 From a45c82b84c844cd85b2ed1aa32596f1bffa32716 Mon Sep 17 00:00:00 2001 From: Ruchika Kharwar Date: Sat, 6 Jul 2013 07:52:49 -0500 Subject: usb: dwc3: adapt to use dr_mode device tree helper This patch adapts the dwc3 to use the device tree helper "of_usb_get_dr_mode" for the mode of operation of the dwc3 instance being probed. [ balbi@ti.com : make of_usb_get_dr_mode() conditional on dev->of_node and let pdata pass dr_mode too ] Reviewed-by: Roger Quadros Signed-off-by: Ruchika Kharwar Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 73 ++++++++++++++++++++-------------------- drivers/usb/dwc3/core.h | 11 +++--- drivers/usb/dwc3/platform_data.h | 2 ++ 3 files changed, 43 insertions(+), 43 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 2f5632730ffb..df3723a1023e 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "platform_data.h" #include "core.h" @@ -359,8 +360,6 @@ static int dwc3_probe(struct platform_device *pdev) void __iomem *regs; void *mem; - u8 mode; - mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); if (!mem) { dev_err(dev, "not enough memory\n"); @@ -415,6 +414,7 @@ static int dwc3_probe(struct platform_device *pdev) dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1); dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); + dwc->dr_mode = of_usb_get_dr_mode(node); } else { dwc->maximum_speed = pdata->maximum_speed; @@ -422,6 +422,7 @@ static int dwc3_probe(struct platform_device *pdev) dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); dwc->needs_fifo_resize = pdata->tx_fifo_resize; + dwc->dr_mode = pdata->dr_mode; } /* default to superspeed if no maximum_speed passed */ @@ -498,14 +499,15 @@ static int dwc3_probe(struct platform_device *pdev) } if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) - mode = DWC3_MODE_HOST; + dwc->dr_mode = USB_DR_MODE_HOST; else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) - mode = DWC3_MODE_DEVICE; - else - mode = DWC3_MODE_DRD; + dwc->dr_mode = USB_DR_MODE_PERIPHERAL; + + if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) + dwc->dr_mode = USB_DR_MODE_OTG; - switch (mode) { - case DWC3_MODE_DEVICE: + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) { @@ -513,7 +515,7 @@ static int dwc3_probe(struct platform_device *pdev) goto err2; } break; - case DWC3_MODE_HOST: + case USB_DR_MODE_HOST: dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); ret = dwc3_host_init(dwc); if (ret) { @@ -521,7 +523,7 @@ static int dwc3_probe(struct platform_device *pdev) goto err2; } break; - case DWC3_MODE_DRD: + case USB_DR_MODE_OTG: dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); ret = dwc3_host_init(dwc); if (ret) { @@ -536,10 +538,9 @@ static int dwc3_probe(struct platform_device *pdev) } break; default: - dev_err(dev, "Unsupported mode of operation %d\n", mode); + dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); goto err2; } - dwc->mode = mode; ret = dwc3_debugfs_init(dwc); if (ret) { @@ -552,14 +553,14 @@ static int dwc3_probe(struct platform_device *pdev) return 0; err3: - switch (mode) { - case DWC3_MODE_DEVICE: + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: dwc3_gadget_exit(dwc); break; - case DWC3_MODE_HOST: + case USB_DR_MODE_HOST: dwc3_host_exit(dwc); break; - case DWC3_MODE_DRD: + case USB_DR_MODE_OTG: dwc3_host_exit(dwc); dwc3_gadget_exit(dwc); break; @@ -592,14 +593,14 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_debugfs_exit(dwc); - switch (dwc->mode) { - case DWC3_MODE_DEVICE: + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: dwc3_gadget_exit(dwc); break; - case DWC3_MODE_HOST: + case USB_DR_MODE_HOST: dwc3_host_exit(dwc); break; - case DWC3_MODE_DRD: + case USB_DR_MODE_OTG: dwc3_host_exit(dwc); dwc3_gadget_exit(dwc); break; @@ -623,12 +624,12 @@ static int dwc3_prepare(struct device *dev) spin_lock_irqsave(&dwc->lock, flags); - switch (dwc->mode) { - case DWC3_MODE_DEVICE: - case DWC3_MODE_DRD: + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: dwc3_gadget_prepare(dwc); /* FALLTHROUGH */ - case DWC3_MODE_HOST: + case USB_DR_MODE_HOST: default: dwc3_event_buffers_cleanup(dwc); break; @@ -646,12 +647,12 @@ static void dwc3_complete(struct device *dev) spin_lock_irqsave(&dwc->lock, flags); - switch (dwc->mode) { - case DWC3_MODE_DEVICE: - case DWC3_MODE_DRD: + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: dwc3_gadget_complete(dwc); /* FALLTHROUGH */ - case DWC3_MODE_HOST: + case USB_DR_MODE_HOST: default: dwc3_event_buffers_setup(dwc); break; @@ -667,12 +668,12 @@ static int dwc3_suspend(struct device *dev) spin_lock_irqsave(&dwc->lock, flags); - switch (dwc->mode) { - case DWC3_MODE_DEVICE: - case DWC3_MODE_DRD: + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: dwc3_gadget_suspend(dwc); /* FALLTHROUGH */ - case DWC3_MODE_HOST: + case USB_DR_MODE_HOST: default: /* do nothing */ break; @@ -700,12 +701,12 @@ static int dwc3_resume(struct device *dev) dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl); - switch (dwc->mode) { - case DWC3_MODE_DEVICE: - case DWC3_MODE_DRD: + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: dwc3_gadget_resume(dwc); /* FALLTHROUGH */ - case DWC3_MODE_HOST: + case USB_DR_MODE_HOST: default: /* do nothing */ break; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 22ada27ad3ff..db385bda3095 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -29,6 +29,7 @@ #include #include +#include /* Global constants */ #define DWC3_EP0_BOUNCE_SIZE 512 @@ -545,11 +546,6 @@ struct dwc3_hwparams { /* HWPARAMS0 */ #define DWC3_MODE(n) ((n) & 0x7) -#define DWC3_MODE_DEVICE 0 -#define DWC3_MODE_HOST 1 -#define DWC3_MODE_DRD 2 -#define DWC3_MODE_HUB 3 - #define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8) /* HWPARAMS1 */ @@ -611,7 +607,7 @@ struct dwc3_scratchpad_array { * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) * @revision: revision register contents - * @mode: mode of operation + * @dr_mode: requested mode of operation * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY * @dcfg: saved contents of DCFG register @@ -669,6 +665,8 @@ struct dwc3 { void __iomem *regs; size_t regs_size; + enum usb_dr_mode dr_mode; + /* used for suspend/resume */ u32 dcfg; u32 gctl; @@ -677,7 +675,6 @@ struct dwc3 { u32 u1u2; u32 maximum_speed; u32 revision; - u32 mode; #define DWC3_REVISION_173A 0x5533173a #define DWC3_REVISION_175A 0x5533175a diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index 16ffe1912523..7db34f00b89a 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -18,8 +18,10 @@ */ #include +#include struct dwc3_platform_data { enum usb_device_speed maximum_speed; + enum usb_dr_mode dr_mode; bool tx_fifo_resize; }; -- cgit v1.2.3-58-ga151 From 636e2a2caeafcb1b55b6799b29fe436039819eb9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 12 Jul 2013 13:19:52 +0300 Subject: usb: dwc3: gadget: drop dwc3 manual phy control Recent versions of the core, can suspend and resume the PHYs automatically, so we don't need to fiddle with dwc3's Global PHY registers at all. On versions prior to 1.94a this patch will mean that we will never ask dwc3 to suspend the PHY. This is an acceptable behavior or such old versions of the core, specially considering that the only chip known to have a version prior to 1.94a was OMAP5 ES1.0 and that's not supported in mainline kernel, because it was just a test spin of OMAP5. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 61 ----------------------------------------------- 1 file changed, 61 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4ca5706f3a86..c7be039d8551 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2105,34 +2105,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) dwc->setup_packet_pending = false; } -static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend) -{ - u32 reg; - - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - - if (suspend) - reg |= DWC3_GUSB3PIPECTL_SUSPHY; - else - reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; - - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); -} - -static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend) -{ - u32 reg; - - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - - if (suspend) - reg |= DWC3_GUSB2PHYCFG_SUSPHY; - else - reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); -} - static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) { u32 reg; @@ -2173,13 +2145,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) /* after reset -> Default State */ usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); - /* Recent versions support automatic phy suspend and don't need this */ - if (dwc->revision < DWC3_REVISION_194A) { - /* Resume PHYs */ - dwc3_gadget_usb2_phy_suspend(dwc, false); - dwc3_gadget_usb3_phy_suspend(dwc, false); - } - if (dwc->gadget.speed != USB_SPEED_UNKNOWN) dwc3_disconnect_gadget(dwc); @@ -2223,20 +2188,6 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) dwc3_writel(dwc->regs, DWC3_GCTL, reg); } -static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed) -{ - switch (speed) { - case USB_SPEED_SUPER: - dwc3_gadget_usb2_phy_suspend(dwc, true); - break; - case USB_SPEED_HIGH: - case USB_SPEED_FULL: - case USB_SPEED_LOW: - dwc3_gadget_usb3_phy_suspend(dwc, true); - break; - } -} - static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) { struct dwc3_ep *dep; @@ -2312,12 +2263,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_DCTL, reg); } - /* Recent versions support automatic phy suspend and don't need this */ - if (dwc->revision < DWC3_REVISION_194A) { - /* Suspend unneeded PHY */ - dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed); - } - dep = dwc->eps[0]; ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true); if (ret) { @@ -2647,12 +2592,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) reg |= DWC3_DCFG_LPM_CAP; dwc3_writel(dwc->regs, DWC3_DCFG, reg); - /* Enable USB2 LPM and automatic phy suspend only on recent versions */ - if (dwc->revision >= DWC3_REVISION_194A) { - dwc3_gadget_usb2_phy_suspend(dwc, false); - dwc3_gadget_usb3_phy_suspend(dwc, false); - } - ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); if (ret) { dev_err(dwc->dev, "failed to register udc\n"); -- cgit v1.2.3-58-ga151 From 8bbcd17d6be41a5c0e4a8562e30c8f15c1ceb836 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 12 Jul 2013 15:33:35 +0300 Subject: usb: dwc3: omap: switch over to devm_ioremap_resource() use the new devm_ioremap_resource on dwc3-omap.c Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index cf209086f3e8..ecd99451ee90 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -389,11 +389,9 @@ static int dwc3_omap_probe(struct platform_device *pdev) return -EINVAL; } - base = devm_ioremap_nocache(dev, res->start, resource_size(res)); - if (!base) { - dev_err(dev, "ioremap failed\n"); - return -ENOMEM; - } + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); spin_lock_init(&omap->lock); -- cgit v1.2.3-58-ga151 From 6c76e6cb48b240ae3062949cf352971b7b03b106 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 12 Jul 2013 15:34:12 +0300 Subject: usb: dwc3: core: switch over to devm_ioremap_resource() use the new devm_ioremap_resource() on core.c Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index df3723a1023e..25d374c54d73 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -389,23 +389,15 @@ static int dwc3_probe(struct platform_device *pdev) dwc->xhci_resources[0].flags = res->flags; dwc->xhci_resources[0].name = res->name; + res->start += DWC3_GLOBALS_REGS_START; + /* * Request memory region but exclude xHCI regs, * since it will be requested by the xhci-plat driver. */ - res = devm_request_mem_region(dev, res->start + DWC3_GLOBALS_REGS_START, - resource_size(res) - DWC3_GLOBALS_REGS_START, - dev_name(dev)); - if (!res) { - dev_err(dev, "can't request mem region\n"); - return -ENOMEM; - } - - regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); - if (!regs) { - dev_err(dev, "ioremap failed\n"); - return -ENOMEM; - } + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); if (node) { dwc->maximum_speed = of_usb_get_maximum_speed(node); -- cgit v1.2.3-58-ga151 From ff62d6b6727360ae73382213bc1ebac6cf5160d7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 12 Jul 2013 19:09:39 +0300 Subject: usb: dwc3: gadget: move debugging print around by moving that dev_vdbg() to the internal __dwc3_gadget_ep_enable() we get the print even when enable ep0, which calls the internal function directly. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c7be039d8551..c8d47c12ce33 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -500,6 +500,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, u32 reg; int ret = -ENOMEM; + dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); + if (!(dep->flags & DWC3_EP_ENABLED)) { ret = dwc3_gadget_start_config(dwc, dep); if (ret) @@ -656,8 +658,6 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, dev_err(dwc->dev, "invalid endpoint transfer type\n"); } - dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); - spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false); spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v1.2.3-58-ga151 From 9aa62ae4292a2450634345d79a7fa1461bf7b39c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 12 Jul 2013 19:10:59 +0300 Subject: usb: dwc3: gadget: move direction setting up no functional changes, just grouping dep initialization. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c8d47c12ce33..f5efa03972bc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1642,13 +1642,13 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, dep->dwc = dwc; dep->number = epnum; + dep->direction = !!direction; dwc->eps[epnum] = dep; snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, (epnum & 1) ? "in" : "out"); dep->endpoint.name = dep->name; - dep->direction = (epnum & 1); if (epnum == 0 || epnum == 1) { dep->endpoint.maxpacket = 512; -- cgit v1.2.3-58-ga151 From 653df35e6b3c0df875999a9b842327301269f55d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 12 Jul 2013 19:11:57 +0300 Subject: usb: dwc3: gadget: add a debugging print when initializing endpoints that way we get debugging information when enabling verbose debug of the driver. It will be no-op otherwise. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f5efa03972bc..3850ff0b853e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1650,6 +1650,8 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, dep->endpoint.name = dep->name; + dev_vdbg(dwc->dev, "initializing %s\n", dep->name); + if (epnum == 0 || epnum == 1) { dep->endpoint.maxpacket = 512; dep->endpoint.maxburst = 1; -- cgit v1.2.3-58-ga151 From aff310d91b537c29b979f975d4d9b5f26e978378 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Jul 2013 11:30:45 +0300 Subject: usb: dwc3: core: don't redefine DWC3_DCFG_LPM_CAP the macro DWC3_DCFG_LPM_CAP was defined twice. This patch just removes one of the definitions, no functional changes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index db385bda3095..cb6f8d803bc5 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -188,7 +188,6 @@ #define DWC3_MAX_HIBER_SCRATCHBUFS 15 /* Device Configuration Register */ -#define DWC3_DCFG_LPM_CAP (1 << 22) #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) -- cgit v1.2.3-58-ga151 From fb49740d72a305f2e4110b4dfb53fdd9130d8878 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Jul 2013 11:31:27 +0300 Subject: usb: dwc3: gadget: don't enable LPM early LPM is enabled in Connection Done interrupt, there's no need to enable it early. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3850ff0b853e..95ef20c5b53b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2540,7 +2540,6 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc) */ int dwc3_gadget_init(struct dwc3 *dwc) { - u32 reg; int ret; dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req), @@ -2590,10 +2589,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) if (ret) goto err4; - reg = dwc3_readl(dwc->regs, DWC3_DCFG); - reg |= DWC3_DCFG_LPM_CAP; - dwc3_writel(dwc->regs, DWC3_DCFG, reg); - ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); if (ret) { dev_err(dwc->dev, "failed to register udc\n"); -- cgit v1.2.3-58-ga151 From 68d6a01bdd2a9ebae858271129d0a8d4ae865e93 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Jun 2013 21:09:26 +0300 Subject: usb: dwc3: core: introduce and use macros for Event Size register That register has more than just the event buffer size; we can also mask and unmask that particular interrupter on bit 31 of that register. In this patch we introduce the necessary macros and make sure to use the new macros while also making sure we mask interrupts during driver removal. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 5 +++-- drivers/usb/dwc3/core.h | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 25d374c54d73..dfbc1f0fc0e3 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -218,7 +218,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), upper_32_bits(evt->dma)); dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), - evt->length & 0xffff); + DWC3_GEVNTSIZ_SIZE(evt->length)); dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); } @@ -237,7 +237,8 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); - dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), DWC3_GEVNTSIZ_INTMASK + | DWC3_GEVNTSIZ_SIZE(0)); dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); } } diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index cb6f8d803bc5..d30e094faa58 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -175,6 +175,10 @@ #define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) #define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) +/* Global Event Size Registers */ +#define DWC3_GEVNTSIZ_INTMASK (1 << 31) +#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff) + /* Global HWPARAMS1 Register */ #define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) #define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 -- cgit v1.2.3-58-ga151 From e8adfc30ff9282a728fd8b666b6418308164c415 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Jun 2013 21:11:14 +0300 Subject: usb: dwc3: gadget: get rid of IRQF_ONESHOT We can make use of bit 31 of the GEVNTSIZ(n) registers to mask/unmask interrupts from that particular interrupter. With that feature, we can easily drop IRQF_ONESHOT from our driver which makes it possible to properly change IRQ priorities when using RT patchset *and* it allows us to make use of the scheduler to choose the proper time to handle this IRQ thread. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 95ef20c5b53b..bdca5a255a1d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1490,7 +1490,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, irq = platform_get_irq(to_platform_device(dwc->dev), 0); ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, - IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc); + IRQF_SHARED, "dwc3", dwc); if (ret) { dev_err(dwc->dev, "failed to request irq #%d --> %d\n", irq, ret); @@ -2447,6 +2447,7 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc) struct dwc3 *dwc = _dwc; unsigned long flags; irqreturn_t ret = IRQ_NONE; + u32 reg; int i; spin_lock_irqsave(&dwc->lock, flags); @@ -2486,6 +2487,11 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc) evt->count = 0; evt->flags &= ~DWC3_EVENT_PENDING; ret = IRQ_HANDLED; + + /* Unmask interrupt */ + reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(i)); + reg &= ~DWC3_GEVNTSIZ_INTMASK; + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(i), reg); } spin_unlock_irqrestore(&dwc->lock, flags); @@ -2497,6 +2503,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) { struct dwc3_event_buffer *evt; u32 count; + u32 reg; evt = dwc->ev_buffs[buf]; @@ -2508,6 +2515,11 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) evt->count = count; evt->flags |= DWC3_EVENT_PENDING; + /* Mask interrupt */ + reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); + reg |= DWC3_GEVNTSIZ_INTMASK; + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); + return IRQ_WAKE_THREAD; } -- cgit v1.2.3-58-ga151 From 7f97aa9865800766e570d0e1be772a034dd30388 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Jun 2013 21:16:11 +0300 Subject: usb: dwc3: gadget: rename dwc3_process_event_buf that function isn't really processing any event buffer, rather just checking whether it contains events to be processed. Due to that reason, we're reaning it to dwc3_check_event_buf() instead. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index bdca5a255a1d..8e47eefffeee 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2499,7 +2499,7 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc) return ret; } -static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) +static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf) { struct dwc3_event_buffer *evt; u32 count; @@ -2534,7 +2534,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc) for (i = 0; i < dwc->num_event_buffers; i++) { irqreturn_t status; - status = dwc3_process_event_buf(dwc, i); + status = dwc3_check_event_buf(dwc, i); if (status == IRQ_WAKE_THREAD) ret = status; } -- cgit v1.2.3-58-ga151 From f42f2447d66233a5bf86e8631e4eb4e863b2ff0b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Jun 2013 21:25:08 +0300 Subject: usb: dwc3: gadget: introduce dwc3_process_event_buf in order to make our IRQ handler thread easier to read, we re-factor the inner loop to a separate function. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 81 +++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 37 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8e47eefffeee..f168eaebdef8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2442,57 +2442,64 @@ static void dwc3_process_event_entry(struct dwc3 *dwc, } } -static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc) +static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) { - struct dwc3 *dwc = _dwc; - unsigned long flags; + struct dwc3_event_buffer *evt; irqreturn_t ret = IRQ_NONE; + int left; u32 reg; - int i; - spin_lock_irqsave(&dwc->lock, flags); + evt = dwc->ev_buffs[buf]; + left = evt->count; - for (i = 0; i < dwc->num_event_buffers; i++) { - struct dwc3_event_buffer *evt; - int left; + if (!(evt->flags & DWC3_EVENT_PENDING)) + return IRQ_NONE; - evt = dwc->ev_buffs[i]; - left = evt->count; + while (left > 0) { + union dwc3_event event; - if (!(evt->flags & DWC3_EVENT_PENDING)) - continue; + event.raw = *(u32 *) (evt->buf + evt->lpos); - while (left > 0) { - union dwc3_event event; + dwc3_process_event_entry(dwc, &event); - event.raw = *(u32 *) (evt->buf + evt->lpos); + /* + * FIXME we wrap around correctly to the next entry as + * almost all entries are 4 bytes in size. There is one + * entry which has 12 bytes which is a regular entry + * followed by 8 bytes data. ATM I don't know how + * things are organized if we get next to the a + * boundary so I worry about that once we try to handle + * that. + */ + evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; + left -= 4; - dwc3_process_event_entry(dwc, &event); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); + } - /* - * FIXME we wrap around correctly to the next entry as - * almost all entries are 4 bytes in size. There is one - * entry which has 12 bytes which is a regular entry - * followed by 8 bytes data. ATM I don't know how - * things are organized if we get next to the a - * boundary so I worry about that once we try to handle - * that. - */ - evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; - left -= 4; + evt->count = 0; + evt->flags &= ~DWC3_EVENT_PENDING; + ret = IRQ_HANDLED; - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(i), 4); - } + /* Unmask interrupt */ + reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); + reg &= ~DWC3_GEVNTSIZ_INTMASK; + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); - evt->count = 0; - evt->flags &= ~DWC3_EVENT_PENDING; - ret = IRQ_HANDLED; + return ret; +} - /* Unmask interrupt */ - reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(i)); - reg &= ~DWC3_GEVNTSIZ_INTMASK; - dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(i), reg); - } +static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc) +{ + struct dwc3 *dwc = _dwc; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + int i; + + spin_lock_irqsave(&dwc->lock, flags); + + for (i = 0; i < dwc->num_event_buffers; i++) + ret |= dwc3_process_event_buf(dwc, i); spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v1.2.3-58-ga151 From c75f52fb26aa9c10b3f5ee157bc0c6c8e8e9bb1a Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Wed, 12 Jun 2013 23:43:11 +0800 Subject: usb: dwc3: fix typo in comment of dwc3_ep Change intervall into interval. Signed-off-by: Huang Rui Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index d30e094faa58..f8af8d44af85 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -398,7 +398,7 @@ struct dwc3_event_buffer { * @number: endpoint number (1 - 15) * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK * @resource_index: Resource transfer index - * @interval: the intervall on which the ISOC transfer is started + * @interval: the interval on which the ISOC transfer is started * @name: a human readable name e.g. ep1out-bulk * @direction: true for TX, false for RX * @stream_capable: true when streams are enabled -- cgit v1.2.3-58-ga151 From 5702f75375aa9ecf8ad3431aef3fe6ce8c8dbd15 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 17 Jul 2013 11:09:49 +0300 Subject: usb: gadget: udc-core: move sysfs_notify() to a workqueue usb_gadget_set_state() will call sysfs_notify() which might sleep. Some users might want to call usb_gadget_set_state() from the very IRQ handler which actually changes the gadget state. Instead of having every UDC driver add their own workqueue for such a simple notification, we're adding it generically to our struct usb_gadget, so the details are hidden from all UDC drivers. Acked-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc-core.c | 12 +++++++++++- include/linux/usb/gadget.h | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index c28ac9872030..3122ab942f75 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -105,11 +106,18 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); /* ------------------------------------------------------------------------- */ +static void usb_gadget_state_work(struct work_struct *work) +{ + struct usb_gadget *gadget = work_to_gadget(work); + + sysfs_notify(&gadget->dev.kobj, NULL, "status"); +} + void usb_gadget_set_state(struct usb_gadget *gadget, enum usb_device_state state) { gadget->state = state; - sysfs_notify(&gadget->dev.kobj, NULL, "status"); + schedule_work(&gadget->work); } EXPORT_SYMBOL_GPL(usb_gadget_set_state); @@ -196,6 +204,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, goto err1; dev_set_name(&gadget->dev, "gadget"); + INIT_WORK(&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent; #ifdef CONFIG_HAS_DMA @@ -315,6 +324,7 @@ found: usb_gadget_remove_driver(udc); kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); + flush_work(&gadget->work); device_unregister(&udc->dev); device_unregister(&gadget->dev); } diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index f1b0dca60f12..942ef5e053bf 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -22,6 +22,7 @@ #include #include #include +#include #include struct usb_ep; @@ -475,6 +476,7 @@ struct usb_gadget_ops { /** * struct usb_gadget - represents a usb slave device + * @work: (internal use) Workqueue to be used for sysfs_notify() * @ops: Function pointers used to access hardware-specific operations. * @ep0: Endpoint zero, used when reading or writing responses to * driver setup() requests @@ -520,6 +522,7 @@ struct usb_gadget_ops { * device is acting as a B-Peripheral (so is_a_peripheral is false). */ struct usb_gadget { + struct work_struct work; /* readonly to gadget driver */ const struct usb_gadget_ops *ops; struct usb_ep *ep0; @@ -538,6 +541,7 @@ struct usb_gadget { unsigned out_epnum; unsigned in_epnum; }; +#define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) static inline void set_gadget_data(struct usb_gadget *gadget, void *data) { dev_set_drvdata(&gadget->dev, data); } -- cgit v1.2.3-58-ga151 From 7a42d83536a5ea7ea7b4ad279c34c4c4ce509b5a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 22 Jul 2013 12:31:31 +0300 Subject: usb: dwc3: ep0: only change to ADDRESS if set_config() succeeds In case we're switching back to USB_STATE_ADDRESS from USB_STATE_CONFIGURED (if host sends a set configuration command for configuration zero), we should only switch if the request is successfully processed by the gadget driver. Reported-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index e9d099cee636..007651c21638 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -551,7 +551,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) case USB_STATE_CONFIGURED: ret = dwc3_ep0_delegate_req(dwc, ctrl); - if (!cfg) + if (!cfg && !ret) usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); break; -- cgit v1.2.3-58-ga151 From 7c81290e87bf01b507deabf0f478e051367cb416 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 22 Jul 2013 12:41:47 +0300 Subject: usb: dwc3: ep0: don't change to configured state too early before changing to configured state, we need to wait until gadget driver has had a chance to process the request. In case of USB_GADGET_DELAYED_STATUS, that means we need to defer usb_gadget_set_state() until the upcoming usb_ep_queue(). Reported-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 007651c21638..7fa93f4bc507 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -148,6 +148,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, direction = !dwc->ep0_expect_in; dwc->delayed_status = false; + usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); if (dwc->ep0state == EP0_STATUS_PHASE) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); @@ -533,8 +534,16 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ret = dwc3_ep0_delegate_req(dwc, ctrl); /* if the cfg matches and the cfg is non zero */ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { - usb_gadget_set_state(&dwc->gadget, - USB_STATE_CONFIGURED); + + /* + * only change state if set_config has already + * been processed. If gadget driver returns + * USB_GADGET_DELAYED_STATUS, we will wait + * to change the state on the next usb_ep_queue() + */ + if (ret == 0) + usb_gadget_set_state(&dwc->gadget, + USB_STATE_CONFIGURED); /* * Enable transition to U1/U2 state when -- cgit v1.2.3-58-ga151 From 052a11d13bdecb77ee4cdbdccfce5434a12209af Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 14 Jun 2013 12:51:41 +0300 Subject: usb: phy: make PHY driver selection possible by controller drivers Convert PHY Drivers from menuconfig to menu so that the PHY drivers can be explicitely selected by the controller drivers. USB_PHY is no longer a user visible option. It is upto to the PHY drivers to select it if needed. This patch does so for the existing PHY drivers that use the USB_PHY library. Doing so moves the USB_PHY and PHY driver selection problem from the end user to the PHY and controller driver developer. e.g. Earlier, a controller driver (e.g. EHCI_OMAP) that needs to select a PHY driver (e.g. NOP_PHY) couldn't do so because the PHY driver depended on USB_PHY. Making the controller driver depend on USB_PHY has a negative effect i.e. it becomes invisible to the user till USB_PHY is enabled. Most end users will not familiar with this. With this patch, the end user just needs to select the controller driver needed for his/her platform without worrying about which PHY driver to select. Also update USB_EHCI_MSM, USB_LPC32XX and USB_OMAP to not depend on USB_PHY any more. They can safely select the necessary PHY drivers. [ balbi@ti.com : refreshed on top of my next branch. Changed bool followed by default n into def_bool n ] CC: Pavankumar Kondeti Acked-by: Roland Stigge Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/Makefile | 2 +- drivers/usb/gadget/Kconfig | 2 -- drivers/usb/host/Kconfig | 1 - drivers/usb/phy/Kconfig | 36 ++++++++++++++++++++---------------- 4 files changed, 21 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 238c5d47cadb..2b3132ede23a 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -45,7 +45,7 @@ obj-$(CONFIG_USB_MICROTEK) += image/ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB) += misc/ -obj-$(CONFIG_USB_PHY) += phy/ +obj-$(CONFIG_USB_SUPPORT) += phy/ obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/ obj-$(CONFIG_USB_ATM) += atm/ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 08dab3c640fd..1e3f52574367 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -144,7 +144,6 @@ config USB_AT91 config USB_LPC32XX tristate "LPC32XX USB Peripheral Controller" depends on ARCH_LPC32XX - depends on USB_PHY select USB_ISP1301 help This option selects the USB device controller in the LPC32xx SoC. @@ -206,7 +205,6 @@ config USB_FOTG210_UDC config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 - depends on USB_PHY select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG help Many Texas Instruments OMAP processors have flexible full diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 4263d011392c..3102b522ca02 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -186,7 +186,6 @@ config USB_EHCI_HCD_AT91 config USB_EHCI_MSM tristate "Support for Qualcomm QSD/MSM on-chip EHCI USB controller" depends on ARCH_MSM - depends on USB_PHY select USB_EHCI_ROOT_HUB_TT select USB_MSM_OTG ---help--- diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 3622fff8b798..f5ea339ec155 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -1,22 +1,10 @@ # # Physical Layer USB driver configuration # -menuconfig USB_PHY - bool "USB Physical Layer drivers" - help - Most USB controllers have the physical layer signalling part - (commonly called a PHY) built in. However, dual-role devices - (a.k.a. USB on-the-go) which support being USB master or slave - with the same connector often use an external PHY. - - The drivers in this submenu add support for such PHY devices. - They are not needed for standard master-only (or the vast - majority of slave-only) USB interfaces. - - If you're not sure if this applies to you, it probably doesn't; - say N here. +menu "USB Physical Layer drivers" -if USB_PHY +config USB_PHY + def_bool n # # USB Transceiver Drivers @@ -24,6 +12,7 @@ if USB_PHY config AB8500_USB tristate "AB8500 USB Transceiver Driver" depends on AB8500_CORE + select USB_PHY help Enable this to support the USB OTG transceiver in AB8500 chip. This transceiver supports high and full speed devices plus, @@ -33,12 +22,14 @@ config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" depends on USB_EHCI_FSL && USB_FSL_USB2 && PM_RUNTIME select USB_OTG + select USB_PHY help Enable this to support Freescale USB OTG transceiver. config ISP1301_OMAP tristate "Philips ISP1301 with OMAP OTG" depends on I2C && ARCH_OMAP_OTG + select USB_PHY help If you say yes here you get support for the Philips ISP1301 USB-On-The-Go transceiver working with the OMAP OTG controller. @@ -52,12 +43,14 @@ config ISP1301_OMAP config MV_U3D_PHY bool "Marvell USB 3.0 PHY controller Driver" depends on CPU_MMP3 + select USB_PHY help Enable this to support Marvell USB 3.0 phy controller for Marvell SoC. config NOP_USB_XCEIV tristate "NOP USB Transceiver Driver" + select USB_PHY help This driver is to be used by all the usb transceiver which are either built-in with usb ip or which are autonomous and doesn't require any @@ -76,6 +69,7 @@ config OMAP_USB2 tristate "OMAP USB2 PHY Driver" depends on ARCH_OMAP2PLUS select OMAP_CONTROL_USB + select USB_PHY help Enable this to support the transceiver that is part of SOC. This driver takes care of all the PHY functionality apart from comparator. @@ -85,6 +79,7 @@ config OMAP_USB2 config OMAP_USB3 tristate "OMAP USB3 PHY Driver" select OMAP_CONTROL_USB + select USB_PHY help Enable this to support the USB3 PHY that is part of SOC. This driver takes care of all the PHY functionality apart from comparator. @@ -101,6 +96,7 @@ config SAMSUNG_USBPHY config SAMSUNG_USB2PHY tristate "Samsung USB 2.0 PHY controller Driver" select SAMSUNG_USBPHY + select USB_PHY help Enable this to support Samsung USB 2.0 (High Speed) PHY controller driver for Samsung SoCs. @@ -108,6 +104,7 @@ config SAMSUNG_USB2PHY config SAMSUNG_USB3PHY tristate "Samsung USB 3.0 PHY controller Driver" select SAMSUNG_USBPHY + select USB_PHY help Enable this to support Samsung USB 3.0 (Super Speed) phy controller for samsung SoCs. @@ -115,6 +112,7 @@ config SAMSUNG_USB3PHY config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS + select USB_PHY help Enable this to support the USB OTG transceiver on TWL4030 family chips (including the TWL5030 and TPS659x0 devices). @@ -135,6 +133,7 @@ config TWL6030_USB config USB_GPIO_VBUS tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" depends on GPIOLIB + select USB_PHY help Provides simple GPIO VBUS sensing for controllers with an internal transceiver via the usb_phy interface, and @@ -145,6 +144,7 @@ config USB_ISP1301 tristate "NXP ISP1301 USB transceiver support" depends on USB || USB_GADGET depends on I2C + select USB_PHY help Say Y here to add support for the NXP ISP1301 USB transceiver driver. This chip is typically used as USB transceiver for USB host, gadget @@ -156,6 +156,7 @@ config USB_ISP1301 config USB_MSM_OTG tristate "OTG support for Qualcomm on-chip USB controller" depends on (USB || USB_GADGET) && ARCH_MSM + select USB_PHY help Enable this to support the USB OTG transceiver on MSM chips. It handles PHY initialization, clock management, and workarounds @@ -169,6 +170,7 @@ config USB_MV_OTG tristate "Marvell USB OTG support" depends on USB_EHCI_MV && USB_MV_UDC && PM_RUNTIME select USB_OTG + select USB_PHY help Say Y here if you want to build Marvell USB OTG transciever driver in kernel (including PXA and MMP series). This driver @@ -180,6 +182,7 @@ config USB_MXS_PHY tristate "Freescale MXS USB PHY support" depends on ARCH_MXC || ARCH_MXS select STMP_DEVICE + select USB_PHY help Enable this to support the Freescale MXS USB PHY. @@ -188,6 +191,7 @@ config USB_MXS_PHY config USB_RCAR_PHY tristate "Renesas R-Car USB PHY support" depends on USB || USB_GADGET + select USB_PHY help Say Y here to add support for the Renesas R-Car USB common PHY driver. This chip is typically used as USB PHY for USB host, gadget. @@ -210,4 +214,4 @@ config USB_ULPI_VIEWPORT Provides read/write operations to the ULPI phy register set for controllers with a viewport register (e.g. Chipidea/ARC controllers). -endif # USB_PHY +endmenu -- cgit v1.2.3-58-ga151 From 88650d62a12de7fb9437b5508cc7f6b28da58cbf Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 14 Jun 2013 12:51:42 +0300 Subject: usb: ehci-omap: select NOP_USB_XCEIV PHY driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ehci-omap needs NOP_USB_XCEIV PHY driver to function properly, so select it. As the USB PHY drivers no longer depend on USB_PHY, it is safe to select the PHY drivers. Signed-off-by: Roger Quadros Tested-by: Adrien Vergé Signed-off-by: Felipe Balbi --- drivers/usb/host/Kconfig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 3102b522ca02..5064e06e25b4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -148,13 +148,11 @@ config USB_EHCI_MXC config USB_EHCI_HCD_OMAP tristate "EHCI support for OMAP3 and later chips" depends on ARCH_OMAP + select NOP_USB_XCEIV default y ---help--- Enables support for the on-chip EHCI controller on OMAP3 and later chips. - If your system uses a PHY on the USB port, you will need to - enable USB_PHY and the appropriate PHY driver as well. Most - boards need the NOP_USB_XCEIV PHY driver. config USB_EHCI_HCD_ORION tristate "Support for Marvell EBU on-chip EHCI USB controller" -- cgit v1.2.3-58-ga151 From 519c6013d356fef95c9e45cfb533b3e68b47dbf4 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 9 Jul 2013 14:38:31 +0300 Subject: usb: phy: omap-usb3: Improve DPLL parameter lookup code Use a mapping table (dpll_map) to match the possible system clock rates to the appropriate DPLL parameters. Introduce a function "omap_usb3_get_dpll_params()" that will return the matching DPLL parameters for the given clock rate. Also, bail out on phy init if DPLL locking fails. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-omap-usb3.c | 87 ++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 49 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-omap-usb3.c b/drivers/usb/phy/phy-omap-usb3.c index a2fb30bbb971..fc15694d3031 100644 --- a/drivers/usb/phy/phy-omap-usb3.c +++ b/drivers/usb/phy/phy-omap-usb3.c @@ -27,7 +27,6 @@ #include #include -#define NUM_SYS_CLKS 6 #define PLL_STATUS 0x00000004 #define PLL_GO 0x00000008 #define PLL_CONFIGURATION1 0x0000000C @@ -57,26 +56,32 @@ */ # define PLL_IDLE_TIME 100; -enum sys_clk_rate { - CLK_RATE_UNDEFINED = -1, - CLK_RATE_12MHZ, - CLK_RATE_16MHZ, - CLK_RATE_19MHZ, - CLK_RATE_20MHZ, - CLK_RATE_26MHZ, - CLK_RATE_38MHZ +struct usb_dpll_map { + unsigned long rate; + struct usb_dpll_params params; }; -static struct usb_dpll_params omap_usb3_dpll_params[NUM_SYS_CLKS] = { - {1250, 5, 4, 20, 0}, /* 12 MHz */ - {3125, 20, 4, 20, 0}, /* 16.8 MHz */ - {1172, 8, 4, 20, 65537}, /* 19.2 MHz */ - {1000, 7, 4, 10, 0}, /* 20 MHz */ - {1250, 12, 4, 20, 0}, /* 26 MHz */ - {3125, 47, 4, 20, 92843}, /* 38.4 MHz */ - +static struct usb_dpll_map dpll_map[] = { + {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */ + {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */ + {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */ + {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */ + {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */ + {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */ }; +static struct usb_dpll_params *omap_usb3_get_dpll_params(unsigned long rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dpll_map); i++) { + if (rate == dpll_map[i].rate) + return &dpll_map[i].params; + } + + return 0; +} + static int omap_usb3_suspend(struct usb_phy *x, int suspend) { struct omap_usb *phy = phy_to_omapusb(x); @@ -116,26 +121,6 @@ static int omap_usb3_suspend(struct usb_phy *x, int suspend) return 0; } -static inline enum sys_clk_rate __get_sys_clk_index(unsigned long rate) -{ - switch (rate) { - case 12000000: - return CLK_RATE_12MHZ; - case 16800000: - return CLK_RATE_16MHZ; - case 19200000: - return CLK_RATE_19MHZ; - case 20000000: - return CLK_RATE_20MHZ; - case 26000000: - return CLK_RATE_26MHZ; - case 38400000: - return CLK_RATE_38MHZ; - default: - return CLK_RATE_UNDEFINED; - } -} - static void omap_usb_dpll_relock(struct omap_usb *phy) { u32 val; @@ -155,39 +140,39 @@ static int omap_usb_dpll_lock(struct omap_usb *phy) { u32 val; unsigned long rate; - enum sys_clk_rate clk_index; - - rate = clk_get_rate(phy->sys_clk); - clk_index = __get_sys_clk_index(rate); + struct usb_dpll_params *dpll_params; - if (clk_index == CLK_RATE_UNDEFINED) { - pr_err("dpll cannot be locked for sys clk freq:%luHz\n", rate); + rate = clk_get_rate(phy->sys_clk); + dpll_params = omap_usb3_get_dpll_params(rate); + if (!dpll_params) { + dev_err(phy->dev, + "No DPLL configuration for %lu Hz SYS CLK\n", rate); return -EINVAL; } val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); val &= ~PLL_REGN_MASK; - val |= omap_usb3_dpll_params[clk_index].n << PLL_REGN_SHIFT; + val |= dpll_params->n << PLL_REGN_SHIFT; omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); val &= ~PLL_SELFREQDCO_MASK; - val |= omap_usb3_dpll_params[clk_index].freq << PLL_SELFREQDCO_SHIFT; + val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); val &= ~PLL_REGM_MASK; - val |= omap_usb3_dpll_params[clk_index].m << PLL_REGM_SHIFT; + val |= dpll_params->m << PLL_REGM_SHIFT; omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); val &= ~PLL_REGM_F_MASK; - val |= omap_usb3_dpll_params[clk_index].mf << PLL_REGM_F_SHIFT; + val |= dpll_params->mf << PLL_REGM_F_SHIFT; omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); val &= ~PLL_SD_MASK; - val |= omap_usb3_dpll_params[clk_index].sd << PLL_SD_SHIFT; + val |= dpll_params->sd << PLL_SD_SHIFT; omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); omap_usb_dpll_relock(phy); @@ -198,8 +183,12 @@ static int omap_usb_dpll_lock(struct omap_usb *phy) static int omap_usb3_init(struct usb_phy *x) { struct omap_usb *phy = phy_to_omapusb(x); + int ret; + + ret = omap_usb_dpll_lock(phy); + if (ret) + return ret; - omap_usb_dpll_lock(phy); omap_control_usb3_phy_power(phy->control_dev, 1); return 0; -- cgit v1.2.3-58-ga151 From 925403f425a4a9c503f2fc295652647b1eb10d82 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 11 Jul 2013 22:32:31 -0700 Subject: usb: renesas_usbhs: tidyup original usbhsx_for_each_xxx macro Current usbhsx_for_each_xxx macro will read out-of-array's memory after last loop operation. It was not good C language operation, and the binary which was compiled by (at least) gcc 4.8.1 is broken This patch tidyup these issues Reported-by: Yusuke Goda Reviewed-by: Takashi Yoshii Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/mod_gadget.c | 6 +++--- drivers/usb/renesas_usbhs/mod_host.c | 6 +++--- drivers/usb/renesas_usbhs/pipe.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 805940c37353..3385aeb5a364 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -77,9 +77,9 @@ struct usbhsg_recip_handle { struct usbhsg_gpriv, mod) #define __usbhsg_for_each_uep(start, pos, g, i) \ - for (i = start, pos = (g)->uep + i; \ - i < (g)->uep_size; \ - i++, pos = (g)->uep + i) + for ((i) = start; \ + ((i) < (g)->uep_size) && ((pos) = (g)->uep + (i)); \ + (i)++) #define usbhsg_for_each_uep(pos, gpriv, i) \ __usbhsg_for_each_uep(1, pos, gpriv, i) diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index b86815421c8d..e40f565004d0 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -111,9 +111,9 @@ static const char usbhsh_hcd_name[] = "renesas_usbhs host"; container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod) #define __usbhsh_for_each_udev(start, pos, h, i) \ - for (i = start, pos = (h)->udev + i; \ - i < USBHSH_DEVICE_MAX; \ - i++, pos = (h)->udev + i) + for ((i) = start; \ + ((i) < USBHSH_DEVICE_MAX) && ((pos) = (h)->udev + (i)); \ + (i)++) #define usbhsh_for_each_udev(pos, hpriv, i) \ __usbhsh_for_each_udev(1, pos, hpriv, i) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index b476fde955bf..3e5349879838 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -54,9 +54,9 @@ struct usbhs_pipe_info { * pipe list */ #define __usbhs_for_each_pipe(start, pos, info, i) \ - for (i = start, pos = (info)->pipe + i; \ - i < (info)->size; \ - i++, pos = (info)->pipe + i) + for ((i) = start; \ + ((i) < (info)->size) && ((pos) = (info)->pipe + (i)); \ + (i)++) #define usbhs_for_each_pipe(pos, priv, i) \ __usbhs_for_each_pipe(1, pos, &((priv)->pipe_info), i) -- cgit v1.2.3-58-ga151 From 0d98b9d6429c21132fa08c452e62127fd5032b55 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 18 Jul 2013 09:43:08 +0200 Subject: usb: gadget: atmel_usba: prepare clk before calling enable Replace clk_enable/disable with clk_prepare_enable/disable_unprepare to avoid common clk framework warnings. Signed-off-by: Boris BREZILLON Signed-off-by: Felipe Balbi --- drivers/usb/gadget/atmel_usba_udc.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 1d9722203ca6..f018017e3a77 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1772,6 +1772,7 @@ out: static int atmel_usba_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { + int ret = 0; struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); unsigned long flags; @@ -1781,8 +1782,14 @@ static int atmel_usba_start(struct usb_gadget *gadget, udc->driver = driver; spin_unlock_irqrestore(&udc->lock, flags); - clk_enable(udc->pclk); - clk_enable(udc->hclk); + ret = clk_prepare_enable(udc->pclk); + if (ret) + goto out; + ret = clk_prepare_enable(udc->hclk); + if (ret) { + clk_disable_unprepare(udc->pclk); + goto out; + } DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name); @@ -1797,9 +1804,11 @@ static int atmel_usba_start(struct usb_gadget *gadget, usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); } + +out: spin_unlock_irqrestore(&udc->lock, flags); - return 0; + return ret; } static int atmel_usba_stop(struct usb_gadget *gadget, @@ -1822,8 +1831,8 @@ static int atmel_usba_stop(struct usb_gadget *gadget, udc->driver = NULL; - clk_disable(udc->hclk); - clk_disable(udc->pclk); + clk_disable_unprepare(udc->hclk); + clk_disable_unprepare(udc->pclk); DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name); @@ -2022,10 +2031,14 @@ static int __init usba_udc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, udc); /* Make sure we start from a clean slate */ - clk_enable(pclk); + ret = clk_prepare_enable(pclk); + if (ret) { + dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n"); + goto err_clk_enable; + } toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); - clk_disable(pclk); + clk_disable_unprepare(pclk); if (pdev->dev.of_node) udc->usba_ep = atmel_udc_of_init(pdev, udc); @@ -2081,6 +2094,7 @@ err_add_udc: free_irq(irq, udc); err_request_irq: err_alloc_ep: +err_clk_enable: iounmap(udc->fifo); err_map_fifo: iounmap(udc->regs); -- cgit v1.2.3-58-ga151 From fda7130354271b55eea50a4f58ea8540c9971295 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 23 Jul 2013 20:01:52 +0200 Subject: usb: phy: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-rcar-usb.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-rcar-usb.c b/drivers/usb/phy/phy-rcar-usb.c index ae909408958d..deb7f97f37c7 100644 --- a/drivers/usb/phy/phy-rcar-usb.c +++ b/drivers/usb/phy/phy-rcar-usb.c @@ -190,11 +190,6 @@ static int rcar_usb_phy_probe(struct platform_device *pdev) } res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res0) { - dev_err(dev, "Not enough platform resources\n"); - return -EINVAL; - } - reg0 = devm_ioremap_resource(dev, res0); if (IS_ERR(reg0)) return PTR_ERR(reg0); -- cgit v1.2.3-58-ga151 From 7557a57f5e649c99239975529e2b30dc4990c548 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Jul 2013 20:09:52 +0200 Subject: usb: musb: dsps: init / shutdown the phy If the init / shutdown function of the phy moves out of dsps into the phy driver, then dsps needs to call the callbacks of the phy driver to ensure that this happens. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 5233804d66b1..603ea747d882 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -428,6 +428,8 @@ static int dsps_musb_init(struct musb *musb) goto err0; } + usb_phy_init(musb->xceiv); + setup_timer(&glue->timer[pdev->id], otg_timer, (unsigned long) musb); /* Reset the musb */ @@ -463,6 +465,7 @@ static int dsps_musb_exit(struct musb *musb) /* Shutdown the on-chip PHY and its PLL. */ musb_dsps_phy_control(glue, pdev->id, 0); + usb_phy_shutdown(musb->xceiv); /* NOP driver needs change if supporting dual instance */ usb_put_phy(musb->xceiv); -- cgit v1.2.3-58-ga151 From e9eb2e08d93afc8f334ffe269fc84ea6257ff02c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Jul 2013 20:09:55 +0200 Subject: usb: musb: remove ti81xx pieces from musb ti81xx does not have a baseport mainline i.e. it should not boot. The amount of rework that is required makes it easier to simply remove that platform (i.e. that possible platform device) and add it later once it comes back with DT support. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 603ea747d882..55f5ff9351d6 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -46,9 +46,7 @@ #include "musb_core.h" -#ifdef CONFIG_OF static const struct of_device_id musb_dsps_of_match[]; -#endif /** * avoid using musb_readx()/musb_writex() as glue layer should not be @@ -753,23 +751,12 @@ static const struct dsps_musb_wrapper ti81xx_driver_data = { .instances = 1, }; -static const struct platform_device_id musb_dsps_id_table[] = { - { - .name = "musb-ti81xx", - .driver_data = (kernel_ulong_t) &ti81xx_driver_data, - }, - { }, /* Terminating Entry */ -}; -MODULE_DEVICE_TABLE(platform, musb_dsps_id_table); - -#ifdef CONFIG_OF static const struct of_device_id musb_dsps_of_match[] = { { .compatible = "ti,musb-am33xx", .data = (void *) &ti81xx_driver_data, }, { }, }; MODULE_DEVICE_TABLE(of, musb_dsps_of_match); -#endif static struct platform_driver dsps_usbss_driver = { .probe = dsps_probe, @@ -779,7 +766,6 @@ static struct platform_driver dsps_usbss_driver = { .pm = &dsps_pm_ops, .of_match_table = of_match_ptr(musb_dsps_of_match), }, - .id_table = musb_dsps_id_table, }; MODULE_DESCRIPTION("TI DSPS MUSB Glue Layer"); -- cgit v1.2.3-58-ga151 From 63c5b4ca7d0daadf11662d6419eb7a1c0ce42eb7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Jul 2013 20:10:04 +0200 Subject: usb: musb: do not change dev's dma_mask Commit 8d2421e ("usb: musb: kill global and static for multi instance") removed the global dma_mask copy and replaced by parent's DMA mask. The problem here is that if the parent does not have a dma_mask then musb_remove() goes kaboom. Instead trying to fix this I was thinking we do we need to erase dma_mask in the first place? Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 9b774e72c0e4..80ffd7ee55df 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1843,10 +1843,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (use_dma && dev->dma_mask) musb->dma_controller = dma_controller_create(musb, musb->mregs); - /* ideally this would be abstracted in platform setup */ - if (!musb->dma_controller) - dev->dma_mask = NULL; - /* be sure interrupts are disabled before connecting ISR */ musb_platform_disable(musb); musb_generic_disable(musb); @@ -1993,9 +1989,6 @@ static int musb_remove(struct platform_device *pdev) musb_free(musb); device_init_wakeup(dev, 0); -#ifndef CONFIG_MUSB_PIO_ONLY - dma_set_mask(dev, *dev->parent->dma_mask); -#endif return 0; } -- cgit v1.2.3-58-ga151 From 81d5dfe6d8b3ba48ffcaa882783185c07b5d2384 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 17 Jul 2013 09:31:01 +0300 Subject: usb: phy: tegra: Read UTMIP parameters from device tree UTMIP parameters used to be hardcoded into tables in the PHY driver. This patch reads them from the device tree instead in accordance with the phy-tegra-usb DT documentation. Signed-off-by: Mikko Perttunen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 129 +++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 42 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index cec0855ed248..3ecd49871b88 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -184,27 +184,6 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { }, }; -static struct tegra_utmip_config utmip_default[] = { - [0] = { - .hssync_start_delay = 9, - .idle_wait_delay = 17, - .elastic_limit = 16, - .term_range_adj = 6, - .xcvr_setup = 9, - .xcvr_lsfslew = 1, - .xcvr_lsrslew = 1, - }, - [2] = { - .hssync_start_delay = 9, - .idle_wait_delay = 17, - .elastic_limit = 16, - .term_range_adj = 6, - .xcvr_setup = 9, - .xcvr_lsfslew = 2, - .xcvr_lsrslew = 2, - }, -}; - static void set_pts(struct tegra_usb_phy *phy, u8 pts_val) { void __iomem *base = phy->regs; @@ -703,13 +682,6 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy) int i; int err; - if (!phy->is_ulpi_phy) { - if (phy->is_legacy_phy) - phy->config = &utmip_default[0]; - else - phy->config = &utmip_default[2]; - } - phy->pll_u = devm_clk_get(phy->dev, "pll_u"); if (IS_ERR(phy->pll_u)) { pr_err("Can't get pll_u clock\n"); @@ -784,6 +756,88 @@ void tegra_ehci_phy_restore_end(struct usb_phy *x) } EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); +static int read_utmi_param(struct platform_device *pdev, const char *param, + u8 *dest) +{ + u32 value; + int err = of_property_read_u32(pdev->dev.of_node, param, &value); + *dest = (u8)value; + if (err < 0) + dev_err(&pdev->dev, "Failed to read USB UTMI parameter %s: %d\n", + param, err); + return err; +} + +static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, + struct platform_device *pdev) +{ + struct resource *res; + int err; + struct tegra_utmip_config *config; + + tegra_phy->is_ulpi_phy = false; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "Failed to get UTMI Pad regs\n"); + return -ENXIO; + } + + tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!tegra_phy->regs) { + dev_err(&pdev->dev, "Failed to remap UTMI Pad regs\n"); + return -ENOMEM; + } + + tegra_phy->config = devm_kzalloc(&pdev->dev, + sizeof(*tegra_phy->config), GFP_KERNEL); + if (!tegra_phy->config) { + dev_err(&pdev->dev, + "unable to allocate memory for USB UTMIP config\n"); + return -ENOMEM; + } + + config = tegra_phy->config; + + err = read_utmi_param(pdev, "nvidia,hssync-start-delay", + &config->hssync_start_delay); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,elastic-limit", + &config->elastic_limit); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,idle-wait-delay", + &config->idle_wait_delay); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,term-range-adj", + &config->term_range_adj); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,xcvr-setup", + &config->xcvr_setup); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew", + &config->xcvr_lsfslew); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,xcvr-lsrslew", + &config->xcvr_lsrslew); + if (err < 0) + return err; + + return 0; +} + static int tegra_usb_phy_probe(struct platform_device *pdev) { struct resource *res; @@ -815,20 +869,9 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) err = of_property_match_string(np, "phy_type", "ulpi"); if (err < 0) { - tegra_phy->is_ulpi_phy = false; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(&pdev->dev, "Failed to get UTMI Pad regs\n"); - return -ENXIO; - } - - tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!tegra_phy->regs) { - dev_err(&pdev->dev, "Failed to remap UTMI Pad regs\n"); - return -ENOMEM; - } + err = utmi_phy_probe(tegra_phy, pdev); + if (err < 0) + return err; } else { tegra_phy->is_ulpi_phy = true; @@ -839,6 +882,8 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->reset_gpio); return tegra_phy->reset_gpio; } + + tegra_phy->config = NULL; } err = of_property_match_string(np, "dr_mode", "otg"); -- cgit v1.2.3-58-ga151 From f5b8c8b6d3b4697f28b818d8784e3e4b2a290022 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 17 Jul 2013 10:37:49 +0300 Subject: usb: tegra: Use regulators instead of GPIOs for USB PHY VBUS The tegra ehci driver has enabled USB vbus regulators directly using GPIOs and the device tree attribute nvidia,vbus-gpio. This is ugly and causes error messages on boot when both the regulator driver and the ehci driver want access to the same GPIO. After this patch, usb vbus regulators for tegra usb phy devices are specified with the device tree attribute vbus-supply = <&x> where x is a regulator defined in the device tree. The old nvidia,vbus-gpio property is no longer supported. Signed-off-by: Mikko Perttunen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/host/ehci-tegra.c | 34 +--------------------------------- drivers/usb/phy/phy-tegra-usb.c | 24 ++++++++++++++++++++++++ include/linux/usb/tegra_usb_phy.h | 1 + 3 files changed, 26 insertions(+), 33 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 6ee7ef79b4f8..14c1f35a4b10 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -322,33 +322,6 @@ static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) free_dma_aligned_buffer(urb); } -static int setup_vbus_gpio(struct platform_device *pdev, - struct tegra_ehci_platform_data *pdata) -{ - int err = 0; - int gpio; - - gpio = pdata->vbus_gpio; - if (!gpio_is_valid(gpio)) - gpio = of_get_named_gpio(pdev->dev.of_node, - "nvidia,vbus-gpio", 0); - if (!gpio_is_valid(gpio)) - return 0; - - err = gpio_request(gpio, "vbus_gpio"); - if (err) { - dev_err(&pdev->dev, "can't request vbus gpio %d", gpio); - return err; - } - err = gpio_direction_output(gpio, 1); - if (err) { - dev_err(&pdev->dev, "can't enable vbus\n"); - return err; - } - - return err; -} - static int tegra_ehci_probe(struct platform_device *pdev) { struct resource *res; @@ -376,14 +349,11 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (!pdev->dev.coherent_dma_mask) pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - setup_vbus_gpio(pdev, pdata); - hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); - err = -ENOMEM; - goto cleanup_vbus_gpio; + return -ENOMEM; } platform_set_drvdata(pdev, hcd); ehci = hcd_to_ehci(hcd); @@ -494,8 +464,6 @@ cleanup_clk_get: clk_put(tegra->clk); cleanup_hcd_create: usb_put_hcd(hcd); -cleanup_vbus_gpio: - /* FIXME: Undo setup_vbus_gpio() here */ return err; } diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 3ecd49871b88..d92a63a5b0c9 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -34,6 +34,7 @@ #include #include #include +#include #define ULPI_VIEWPORT 0x170 @@ -613,6 +614,9 @@ static void tegra_usb_phy_close(struct usb_phy *x) { struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + if (!IS_ERR(phy->vbus)) + regulator_disable(phy->vbus); + clk_disable_unprepare(phy->pll_u); } @@ -705,6 +709,16 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy) goto fail; } + if (!IS_ERR(phy->vbus)) { + err = regulator_enable(phy->vbus); + if (err) { + dev_err(phy->dev, + "failed to enable usb vbus regulator: %d\n", + err); + goto fail; + } + } + if (phy->is_ulpi_phy) err = ulpi_open(phy); else @@ -896,6 +910,16 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) } else tegra_phy->mode = TEGRA_USB_PHY_MODE_OTG; + /* On some boards, the VBUS regulator doesn't need to be controlled */ + if (of_find_property(np, "vbus-supply", NULL)) { + tegra_phy->vbus = devm_regulator_get(&pdev->dev, "vbus"); + if (IS_ERR(tegra_phy->vbus)) + return PTR_ERR(tegra_phy->vbus); + } else { + dev_notice(&pdev->dev, "no vbus regulator"); + tegra_phy->vbus = ERR_PTR(-ENODEV); + } + tegra_phy->dev = &pdev->dev; err = tegra_usb_phy_init(tegra_phy); if (err < 0) diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index d2ca919a5b73..2b5fa947980a 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -55,6 +55,7 @@ struct tegra_usb_phy { struct clk *clk; struct clk *pll_u; struct clk *pad_clk; + struct regulator *vbus; enum tegra_usb_phy_mode mode; void *config; struct usb_phy *ulpi; -- cgit v1.2.3-58-ga151 From 185d0fd570c121c0c3d4527842a097e50e3e7a79 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:01 +0300 Subject: usb: phy: tegra: Remove unnecessary 'dev' field struct usb_phy already has a field for the device pointer, so this unnecessary field can be removed. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 27 +++++++++++++++------------ include/linux/usb/tegra_usb_phy.h | 1 - 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index d92a63a5b0c9..311fe03f16e1 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -211,7 +211,7 @@ static void set_phcd(struct tegra_usb_phy *phy, bool enable) static int utmip_pad_open(struct tegra_usb_phy *phy) { - phy->pad_clk = devm_clk_get(phy->dev, "utmi-pads"); + phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads"); if (IS_ERR(phy->pad_clk)) { pr_err("%s: can't get utmip pad clock\n", __func__); return PTR_ERR(phy->pad_clk); @@ -540,13 +540,15 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) ret = gpio_direction_output(phy->reset_gpio, 0); if (ret < 0) { - dev_err(phy->dev, "gpio %d not set to 0\n", phy->reset_gpio); + dev_err(phy->u_phy.dev, "gpio %d not set to 0\n", + phy->reset_gpio); return ret; } msleep(5); ret = gpio_direction_output(phy->reset_gpio, 1); if (ret < 0) { - dev_err(phy->dev, "gpio %d not set to 1\n", phy->reset_gpio); + dev_err(phy->u_phy.dev, "gpio %d not set to 1\n", + phy->reset_gpio); return ret; } @@ -649,29 +651,30 @@ static int ulpi_open(struct tegra_usb_phy *phy) { int err; - phy->clk = devm_clk_get(phy->dev, "ulpi-link"); + phy->clk = devm_clk_get(phy->u_phy.dev, "ulpi-link"); if (IS_ERR(phy->clk)) { pr_err("%s: can't get ulpi clock\n", __func__); return PTR_ERR(phy->clk); } - err = devm_gpio_request(phy->dev, phy->reset_gpio, "ulpi_phy_reset_b"); + err = devm_gpio_request(phy->u_phy.dev, phy->reset_gpio, + "ulpi_phy_reset_b"); if (err < 0) { - dev_err(phy->dev, "request failed for gpio: %d\n", + dev_err(phy->u_phy.dev, "request failed for gpio: %d\n", phy->reset_gpio); return err; } err = gpio_direction_output(phy->reset_gpio, 0); if (err < 0) { - dev_err(phy->dev, "gpio %d direction not set to output\n", + dev_err(phy->u_phy.dev, "gpio %d direction not set to output\n", phy->reset_gpio); return err; } phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); if (!phy->ulpi) { - dev_err(phy->dev, "otg_ulpi_create returned NULL\n"); + dev_err(phy->u_phy.dev, "otg_ulpi_create returned NULL\n"); err = -ENOMEM; return err; } @@ -686,7 +689,7 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy) int i; int err; - phy->pll_u = devm_clk_get(phy->dev, "pll_u"); + phy->pll_u = devm_clk_get(phy->u_phy.dev, "pll_u"); if (IS_ERR(phy->pll_u)) { pr_err("Can't get pll_u clock\n"); return PTR_ERR(phy->pll_u); @@ -712,7 +715,7 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy) if (!IS_ERR(phy->vbus)) { err = regulator_enable(phy->vbus); if (err) { - dev_err(phy->dev, + dev_err(phy->u_phy.dev, "failed to enable usb vbus regulator: %d\n", err); goto fail; @@ -920,7 +923,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->vbus = ERR_PTR(-ENODEV); } - tegra_phy->dev = &pdev->dev; + tegra_phy->u_phy.dev = &pdev->dev; err = tegra_usb_phy_init(tegra_phy); if (err < 0) return err; @@ -953,7 +956,7 @@ static int tegra_usb_phy_match(struct device *dev, void *data) struct tegra_usb_phy *tegra_phy = dev_get_drvdata(dev); struct device_node *dn = data; - return (tegra_phy->dev->of_node == dn) ? 1 : 0; + return (tegra_phy->u_phy.dev->of_node == dn) ? 1 : 0; } struct usb_phy *tegra_usb_get_phy(struct device_node *dn) diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 2b5fa947980a..c2bc7106aaa8 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -60,7 +60,6 @@ struct tegra_usb_phy { void *config; struct usb_phy *ulpi; struct usb_phy u_phy; - struct device *dev; bool is_legacy_phy; bool is_ulpi_phy; int reset_gpio; -- cgit v1.2.3-58-ga151 From de3f233703080bfbbd5861d27660db7a2e476238 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:02 +0300 Subject: usb: host: tegra: Remove leftover code ehci-tegra calls devm_usb_get_phy, which will never succeed since the Tegra PHY does not register itself with the PHY subsystem. It is also completely redundant since the code has already located a PHY via an internal API. Call otg_set_host unconditionally to simplify the code since it should be safe to do so. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/host/ehci-tegra.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 14c1f35a4b10..06e8febdcc2c 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -58,7 +58,6 @@ static int (*orig_hub_control)(struct usb_hcd *hcd, struct tegra_ehci_hcd { struct tegra_usb_phy *phy; struct clk *clk; - struct usb_phy *transceiver; int port_resuming; bool needs_double_reset; enum tegra_usb_phy_port_speed port_speed; @@ -436,26 +435,18 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto cleanup_phy; } - if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = - devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (!IS_ERR(tegra->transceiver)) - otg_set_host(tegra->transceiver->otg, &hcd->self); - } else { - tegra->transceiver = ERR_PTR(-ENODEV); - } + otg_set_host(u_phy->otg, &hcd->self); err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto cleanup_transceiver; + goto cleanup_otg_set_host; } return err; -cleanup_transceiver: - if (!IS_ERR(tegra->transceiver)) - otg_set_host(tegra->transceiver->otg, NULL); +cleanup_otg_set_host: + otg_set_host(u_phy->otg, NULL); cleanup_phy: usb_phy_shutdown(hcd->phy); cleanup_clk_en: @@ -473,8 +464,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv; - if (!IS_ERR(tegra->transceiver)) - otg_set_host(tegra->transceiver->otg, NULL); + otg_set_host(hcd->phy->otg, NULL); usb_phy_shutdown(hcd->phy); usb_remove_hcd(hcd); -- cgit v1.2.3-58-ga151 From d506427945089ced99cfbc5f53dd5adc5d34a5cd Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:03 +0300 Subject: usb: tegra: host: Remove references to plat data Platform data is not used in tegra-ehci anymore, so remove all references to it. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/host/ehci-tegra.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 06e8febdcc2c..a208cea4e4c7 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -327,18 +326,11 @@ static int tegra_ehci_probe(struct platform_device *pdev) struct usb_hcd *hcd; struct ehci_hcd *ehci; struct tegra_ehci_hcd *tegra; - struct tegra_ehci_platform_data *pdata; int err = 0; int irq; struct device_node *np_phy; struct usb_phy *u_phy; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "Platform data missing\n"); - return -EINVAL; - } - /* Right now device-tree probed devices don't get dma_mask set. * Since shared usb code relies on it, set it here for now. * Once we have dma capability bindings this can go away. -- cgit v1.2.3-58-ga151 From 0ee5b4ab79b6be1400f3fa47142e927ddc33a5bd Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:05 +0300 Subject: usb: phy: tegra: Register as an USB PHY. Register the Tegra PHY device instances with the PHY subsystem so that the Tegra EHCI driver can locate a PHY via the standard APIs. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 311fe03f16e1..441c306c050f 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -932,6 +932,22 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend; dev_set_drvdata(&pdev->dev, tegra_phy); + + err = usb_add_phy_dev(&tegra_phy->u_phy); + if (err < 0) { + tegra_usb_phy_close(&tegra_phy->u_phy); + return err; + } + + return 0; +} + +static int tegra_usb_phy_remove(struct platform_device *pdev) +{ + struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev); + + usb_remove_phy(&tegra_phy->u_phy); + return 0; } @@ -943,6 +959,7 @@ MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table); static struct platform_driver tegra_usb_phy_driver = { .probe = tegra_usb_phy_probe, + .remove = tegra_usb_phy_remove, .driver = { .name = "tegra-phy", .owner = THIS_MODULE, -- cgit v1.2.3-58-ga151 From 7db71a9a6707284f3d9075fff2ad674f6a51c359 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:06 +0300 Subject: usb: host: tegra: Locate a PHY via standard API Use devm_get_phy_by_phandle to get a PHY device instead of the custom Tegra functions. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/host/ehci-tegra.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index a208cea4e4c7..db8031fd5c13 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -328,7 +328,6 @@ static int tegra_ehci_probe(struct platform_device *pdev) struct tegra_ehci_hcd *tegra; int err = 0; int irq; - struct device_node *np_phy; struct usb_phy *u_phy; /* Right now device-tree probed devices don't get dma_mask set. @@ -367,13 +366,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) udelay(1); tegra_periph_reset_deassert(tegra->clk); - np_phy = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0); - if (!np_phy) { - err = -ENODEV; - goto cleanup_clk_en; - } - - u_phy = tegra_usb_get_phy(np_phy); + u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); if (IS_ERR(u_phy)) { err = PTR_ERR(u_phy); goto cleanup_clk_en; -- cgit v1.2.3-58-ga151 From 3b102e8bc0e49d417c7d376af857537080335dbf Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:07 +0300 Subject: usb: phy: tegra: Remove custom PHY locating APIs The Tegra EHCI driver is no longer using these custom functions, so they can be removed. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 24 ------------------------ include/linux/usb/tegra_usb_phy.h | 2 -- 2 files changed, 26 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 441c306c050f..fb5bc8cea680 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -968,29 +968,5 @@ static struct platform_driver tegra_usb_phy_driver = { }; module_platform_driver(tegra_usb_phy_driver); -static int tegra_usb_phy_match(struct device *dev, void *data) -{ - struct tegra_usb_phy *tegra_phy = dev_get_drvdata(dev); - struct device_node *dn = data; - - return (tegra_phy->u_phy.dev->of_node == dn) ? 1 : 0; -} - -struct usb_phy *tegra_usb_get_phy(struct device_node *dn) -{ - struct device *dev; - struct tegra_usb_phy *tegra_phy; - - dev = driver_find_device(&tegra_usb_phy_driver.driver, NULL, dn, - tegra_usb_phy_match); - if (!dev) - return ERR_PTR(-EPROBE_DEFER); - - tegra_phy = dev_get_drvdata(dev); - - return &tegra_phy->u_phy; -} -EXPORT_SYMBOL_GPL(tegra_usb_get_phy); - MODULE_DESCRIPTION("Tegra USB PHY driver"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 847415313ad2..a095c98184d0 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -60,8 +60,6 @@ struct tegra_usb_phy { int reset_gpio; }; -struct usb_phy *tegra_usb_get_phy(struct device_node *dn); - void tegra_usb_phy_preresume(struct usb_phy *phy); void tegra_usb_phy_postresume(struct usb_phy *phy); -- cgit v1.2.3-58-ga151 From 9fdb07f72088ab5fc7bd6fd6f070fac53d636bd9 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:08 +0300 Subject: usb: phy: tegra: Use DT helpers for phy_type Use the new of_usb_get_phy_mode helper function for parsing phy_type from the device tree. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index fb5bc8cea680..fb469f1e8d10 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -860,6 +861,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) struct resource *res; struct tegra_usb_phy *tegra_phy = NULL; struct device_node *np = pdev->dev.of_node; + enum usb_phy_interface phy_type; int err; tegra_phy = devm_kzalloc(&pdev->dev, sizeof(*tegra_phy), GFP_KERNEL); @@ -884,12 +886,12 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->is_legacy_phy = of_property_read_bool(np, "nvidia,has-legacy-mode"); - err = of_property_match_string(np, "phy_type", "ulpi"); - if (err < 0) { + phy_type = of_usb_get_phy_mode(np); + if (phy_type == USBPHY_INTERFACE_MODE_UTMI) { err = utmi_phy_probe(tegra_phy, pdev); if (err < 0) return err; - } else { + } else if (phy_type == USBPHY_INTERFACE_MODE_ULPI) { tegra_phy->is_ulpi_phy = true; tegra_phy->reset_gpio = @@ -899,8 +901,10 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->reset_gpio); return tegra_phy->reset_gpio; } - tegra_phy->config = NULL; + } else { + dev_err(&pdev->dev, "phy_type is invalid or unsupported\n"); + return -EINVAL; } err = of_property_match_string(np, "dr_mode", "otg"); -- cgit v1.2.3-58-ga151 From 6558d7edbe069df1220b525362d4aa854a65a8bc Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:09 +0300 Subject: usb: phy: tegra: Use DT helpers for dr_mode Use the new of_usb_get_dr_mode helper function for parsing dr_mode from the device tree. Also replace the usage of the custom tegra_usb_phy_mode enum with the standard enum. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 24 ++++++++++++------------ include/linux/usb/tegra_usb_phy.h | 8 +------- 2 files changed, 13 insertions(+), 19 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index fb469f1e8d10..01c30ff8874e 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -377,7 +377,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); writel(val, base + UTMIP_PLL_CFG1); - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { + if (phy->mode == USB_DR_MODE_PERIPHERAL) { val = readl(base + USB_SUSP_CTRL); val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV); writel(val, base + USB_SUSP_CTRL); @@ -412,7 +412,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) if (phy->is_legacy_phy) { val = readl(base + UTMIP_SPARE_CFG0); - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) + if (phy->mode == USB_DR_MODE_PERIPHERAL) val &= ~FUSE_SETUP_SEL; else val |= FUSE_SETUP_SEL; @@ -453,7 +453,7 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy) utmi_phy_clk_disable(phy); - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { + if (phy->mode == USB_DR_MODE_PERIPHERAL) { val = readl(base + USB_SUSP_CTRL); val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5); @@ -907,15 +907,15 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return -EINVAL; } - err = of_property_match_string(np, "dr_mode", "otg"); - if (err < 0) { - err = of_property_match_string(np, "dr_mode", "peripheral"); - if (err < 0) - tegra_phy->mode = TEGRA_USB_PHY_MODE_HOST; - else - tegra_phy->mode = TEGRA_USB_PHY_MODE_DEVICE; - } else - tegra_phy->mode = TEGRA_USB_PHY_MODE_OTG; + if (of_find_property(np, "dr_mode", NULL)) + tegra_phy->mode = of_usb_get_dr_mode(np); + else + tegra_phy->mode = USB_DR_MODE_HOST; + + if (tegra_phy->mode == USB_DR_MODE_UNKNOWN) { + dev_err(&pdev->dev, "dr_mode is invalid\n"); + return -EINVAL; + } /* On some boards, the VBUS regulator doesn't need to be controlled */ if (of_find_property(np, "vbus-supply", NULL)) { diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index a095c98184d0..d3db274610a1 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -34,12 +34,6 @@ enum tegra_usb_phy_port_speed { TEGRA_USB_PHY_PORT_SPEED_HIGH, }; -enum tegra_usb_phy_mode { - TEGRA_USB_PHY_MODE_DEVICE, - TEGRA_USB_PHY_MODE_HOST, - TEGRA_USB_PHY_MODE_OTG, -}; - struct tegra_xtal_freq; struct tegra_usb_phy { @@ -51,7 +45,7 @@ struct tegra_usb_phy { struct clk *pll_u; struct clk *pad_clk; struct regulator *vbus; - enum tegra_usb_phy_mode mode; + enum usb_dr_mode mode; void *config; struct usb_phy *ulpi; struct usb_phy u_phy; -- cgit v1.2.3-58-ga151 From fa7b4ca50f03b6e71665ccef66ca000311353fc8 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 26 Jul 2013 11:11:37 +0200 Subject: usb: musb: dsps: rename ti81xx_driver_data to am33xx_driver_data The ti81xx platform is not fully supported right now. This patch renames the date structure to a am33xx prefix which is actually used. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 55f5ff9351d6..aba2dc58938d 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -720,7 +720,7 @@ static int dsps_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume); -static const struct dsps_musb_wrapper ti81xx_driver_data = { +static const struct dsps_musb_wrapper am33xx_driver_data = { .revision = 0x00, .control = 0x14, .status = 0x18, @@ -753,7 +753,7 @@ static const struct dsps_musb_wrapper ti81xx_driver_data = { static const struct of_device_id musb_dsps_of_match[] = { { .compatible = "ti,musb-am33xx", - .data = (void *) &ti81xx_driver_data, }, + .data = (void *) &am33xx_driver_data, }, { }, }; MODULE_DEVICE_TABLE(of, musb_dsps_of_match); -- cgit v1.2.3-58-ga151 From 9be73bae70ac5b0149daa243eeae2bdacd970574 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 26 Jul 2013 11:11:38 +0200 Subject: usb: musb: dsps: remove EOI access The EOI register is not present in the AM335x memory space according to the TRM and thus removed. Should any platform using the EOI register get merged then it may be used again if the register address is not zero. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index aba2dc58938d..23f511f7df6d 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -73,7 +73,6 @@ struct dsps_musb_wrapper { u16 revision; u16 control; u16 status; - u16 eoi; u16 epintr_set; u16 epintr_clear; u16 epintr_status; @@ -203,7 +202,6 @@ static void dsps_musb_disable(struct musb *musb) dsps_writel(reg_base, wrp->epintr_clear, wrp->txep_bitmap | wrp->rxep_bitmap); dsps_writeb(musb->mregs, MUSB_DEVCTL, 0); - dsps_writel(reg_base, wrp->eoi, 0); } static void otg_timer(unsigned long _musb) @@ -317,7 +315,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) /* Get usb core interrupts */ usbintr = dsps_readl(reg_base, wrp->coreintr_status); if (!usbintr && !epintr) - goto eoi; + goto out; musb->int_usb = (usbintr & wrp->usb_bitmap) >> wrp->usb_shift; if (usbintr) @@ -385,16 +383,11 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) if (musb->int_tx || musb->int_rx || musb->int_usb) ret |= musb_interrupt(musb); - eoi: - /* EOI needs to be written for the IRQ to be re-asserted. */ - if (ret == IRQ_HANDLED || epintr || usbintr) - dsps_writel(reg_base, wrp->eoi, 1); - /* Poll for ID change */ if (musb->xceiv->state == OTG_STATE_B_IDLE) mod_timer(&glue->timer[pdev->id], jiffies + wrp->poll_seconds * HZ); - +out: spin_unlock_irqrestore(&musb->lock, flags); return ret; @@ -443,9 +436,6 @@ static int dsps_musb_init(struct musb *musb) val &= ~(1 << wrp->otg_disable); dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); - /* clear level interrupt */ - dsps_writel(reg_base, wrp->eoi, 0); - return 0; err0: usb_put_phy(musb->xceiv); @@ -724,7 +714,6 @@ static const struct dsps_musb_wrapper am33xx_driver_data = { .revision = 0x00, .control = 0x14, .status = 0x18, - .eoi = 0x24, .epintr_set = 0x38, .epintr_clear = 0x40, .epintr_status = 0x30, -- cgit v1.2.3-58-ga151 From a554aea67aaadb7f3396b2a8f940ab0b915b2a5e Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 22:24:09 +0300 Subject: usb: phy: tegra: Use switch instead of if-else Use switch() instead of if-else when checking for the PHY type. Signed-off-by: Tuomas Tynkkynen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 01c30ff8874e..49fa2da56c4b 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -887,11 +887,14 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) of_property_read_bool(np, "nvidia,has-legacy-mode"); phy_type = of_usb_get_phy_mode(np); - if (phy_type == USBPHY_INTERFACE_MODE_UTMI) { + switch (phy_type) { + case USBPHY_INTERFACE_MODE_UTMI: err = utmi_phy_probe(tegra_phy, pdev); if (err < 0) return err; - } else if (phy_type == USBPHY_INTERFACE_MODE_ULPI) { + break; + + case USBPHY_INTERFACE_MODE_ULPI: tegra_phy->is_ulpi_phy = true; tegra_phy->reset_gpio = @@ -902,7 +905,9 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return tegra_phy->reset_gpio; } tegra_phy->config = NULL; - } else { + break; + + default: dev_err(&pdev->dev, "phy_type is invalid or unsupported\n"); return -EINVAL; } -- cgit v1.2.3-58-ga151 From 2e112345c22bcdf5d246db40f0587d7d11f1dc61 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 29 Jul 2013 10:27:02 +0300 Subject: usb: dwc3: core: modify IO memory resource after deferred probe completes When deferred probe happens driver will try to ioremap multiple times and will fail. Memory resource.start variable is a global variable, modifications in this field will be accumulated on every probe. Fix this by moving the above operations after driver hold all required PHY's. Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index dfbc1f0fc0e3..da0a4b8b33ed 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -384,21 +384,6 @@ static int dwc3_probe(struct platform_device *pdev) dev_err(dev, "missing memory resource\n"); return -ENODEV; } - dwc->xhci_resources[0].start = res->start; - dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + - DWC3_XHCI_REGS_END; - dwc->xhci_resources[0].flags = res->flags; - dwc->xhci_resources[0].name = res->name; - - res->start += DWC3_GLOBALS_REGS_START; - - /* - * Request memory region but exclude xHCI regs, - * since it will be requested by the xhci-plat driver. - */ - regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) - return PTR_ERR(regs); if (node) { dwc->maximum_speed = of_usb_get_maximum_speed(node); @@ -452,6 +437,22 @@ static int dwc3_probe(struct platform_device *pdev) return -EPROBE_DEFER; } + dwc->xhci_resources[0].start = res->start; + dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + + DWC3_XHCI_REGS_END; + dwc->xhci_resources[0].flags = res->flags; + dwc->xhci_resources[0].name = res->name; + + res->start += DWC3_GLOBALS_REGS_START; + + /* + * Request memory region but exclude xHCI regs, + * since it will be requested by the xhci-plat driver. + */ + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); -- cgit v1.2.3-58-ga151 From 02f00c4a9183ebf15aa50e57391b0f44e2f092c2 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Mon, 29 Jul 2013 18:33:22 +0200 Subject: USB: serial: pl2303: fix the upper baud rate limit check for type_0/1 chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following regression that has been introduced recently with commit b2d6d98fc7: With type_0 and type_1 chips - all baud rates < 1228800 baud are rounded up to 1228800 baud - the device silently runs at 9600 baud for all baud rates > 1228800 baud Signed-off-by: Frank Schäfer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 299a0ffcb165..1e6de4cd079d 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -303,7 +303,7 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, /* type_0, type_1 only support up to 1228800 baud */ if (spriv->type != HX) - baud = max_t(int, baud, 1228800); + baud = min_t(int, baud, 1228800); if (baud <= 115200) { put_unaligned_le32(baud, buf); -- cgit v1.2.3-58-ga151 From 7d50195f6c5005d6ae7a789d9a7f0a94d104ee96 Mon Sep 17 00:00:00 2001 From: Feng-Hsin Chiang Date: Mon, 29 Jul 2013 16:48:32 +0000 Subject: usb: host: Faraday fotg210-hcd driver FOTG210 is an OTG controller which can be configured as an USB2.0 host. FOTG210 host is an ehci-like controller with some differences. First, register layout of FOTG210 is incompatible with EHCI. Furthermore, FOTG210 is lack of siTDs which means iTDs are used for both HS and FS ISO transfer. Signed-off-by: Feng-Hsin Chiang Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 + drivers/usb/host/Kconfig | 12 + drivers/usb/host/Makefile | 1 + drivers/usb/host/fotg210-hcd.c | 6049 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/fotg210.h | 750 +++++ 5 files changed, 6813 insertions(+) create mode 100644 drivers/usb/host/fotg210-hcd.c create mode 100644 drivers/usb/host/fotg210.h (limited to 'drivers/usb') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 238c5d47cadb..5460abf045de 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ obj-$(CONFIG_USB_FUSBH200_HCD) += host/ +obj-$(CONFIG_USB_FOTG210_HCD) += host/ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 4263d011392c..cf521d6551dd 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -354,6 +354,18 @@ config USB_FUSBH200_HCD To compile this driver as a module, choose M here: the module will be called fusbh200-hcd. +config USB_FOTG210_HCD + tristate "FOTG210 HCD support" + depends on USB + default N + ---help--- + Faraday FOTG210 is an OTG controller which can be configured as + an USB2.0 host. It is designed to meet USB2.0 EHCI specification + with minor modification. + + To compile this driver as a module, choose M here: the + module will be called fotg210-hcd. + config USB_OHCI_HCD tristate "OHCI HCD (USB 1.1) support" select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index bea71127b15f..829a3397882a 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o obj-$(CONFIG_USB_FUSBH200_HCD) += fusbh200-hcd.o +obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c new file mode 100644 index 000000000000..fce13bcc4a3e --- /dev/null +++ b/drivers/usb/host/fotg210-hcd.c @@ -0,0 +1,6049 @@ +/* + * Faraday FOTG210 EHCI-like driver + * + * Copyright (c) 2013 Faraday Technology Corporation + * + * Author: Yuan-Hsin Chen + * Feng-Hsin Chiang + * Po-Yu Chuang + * + * Most of code borrowed from the Linux-3.7 EHCI driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/*-------------------------------------------------------------------------*/ +#define DRIVER_AUTHOR "Yuan-Hsin Chen" +#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" + +static const char hcd_name[] = "fotg210_hcd"; + +#undef VERBOSE_DEBUG +#undef FOTG210_URB_TRACE + +#ifdef DEBUG +#define FOTG210_STATS +#endif + +/* magic numbers that can affect system performance */ +#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define FOTG210_TUNE_RL_TT 0 +#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define FOTG210_TUNE_MULT_TT 1 +/* + * Some drivers think it's safe to schedule isochronous transfers more than + * 256 ms into the future (partly as a result of an old bug in the scheduling + * code). In an attempt to avoid trouble, we will use a minimum scheduling + * length of 512 frames instead of 256. + */ +#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ + +/* Initial IRQ latency: faster than hw default */ +static int log2_irq_thresh; /* 0 to 6 */ +module_param(log2_irq_thresh, int, S_IRUGO); +MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); + +/* initial park setting: slower than hw default */ +static unsigned park; +module_param(park, uint, S_IRUGO); +MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); + +/* for link power management(LPM) feature */ +static unsigned int hird; +module_param(hird, int, S_IRUGO); +MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); + +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) + +#include "fotg210.h" + +/*-------------------------------------------------------------------------*/ + +#define fotg210_dbg(fotg210, fmt, args...) \ + dev_dbg(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) +#define fotg210_err(fotg210, fmt, args...) \ + dev_err(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) +#define fotg210_info(fotg210, fmt, args...) \ + dev_info(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) +#define fotg210_warn(fotg210, fmt, args...) \ + dev_warn(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) + +#ifdef VERBOSE_DEBUG +# define fotg210_vdbg fotg210_dbg +#else + static inline void fotg210_vdbg(struct fotg210_hcd *fotg210, ...) {} +#endif + +#ifdef DEBUG + +/* check the values in the HCSPARAMS register + * (host controller _Structural_ parameters) + * see EHCI spec, Table 2-4 for each value + */ +static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) +{ + u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); + + fotg210_dbg(fotg210, + "%s hcs_params 0x%x ports=%d\n", + label, params, + HCS_N_PORTS(params) + ); +} +#else + +static inline void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) {} + +#endif + +#ifdef DEBUG + +/* check the values in the HCCPARAMS register + * (host controller _Capability_ parameters) + * see EHCI Spec, Table 2-5 for each value + * */ +static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) +{ + u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); + + fotg210_dbg(fotg210, + "%s hcc_params %04x uframes %s%s\n", + label, + params, + HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", + HCC_CANPARK(params) ? " park" : ""); +} +#else + +static inline void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) {} + +#endif + +#ifdef DEBUG + +static void __maybe_unused +dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) +{ + fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, + hc32_to_cpup(fotg210, &qtd->hw_next), + hc32_to_cpup(fotg210, &qtd->hw_alt_next), + hc32_to_cpup(fotg210, &qtd->hw_token), + hc32_to_cpup(fotg210, &qtd->hw_buf[0])); + if (qtd->hw_buf[1]) + fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", + hc32_to_cpup(fotg210, &qtd->hw_buf[1]), + hc32_to_cpup(fotg210, &qtd->hw_buf[2]), + hc32_to_cpup(fotg210, &qtd->hw_buf[3]), + hc32_to_cpup(fotg210, &qtd->hw_buf[4])); +} + +static void __maybe_unused +dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + struct fotg210_qh_hw *hw = qh->hw; + + fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, + qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current); + dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); +} + +static void __maybe_unused +dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) +{ + fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", + label, itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), + itd->urb); + fotg210_dbg(fotg210, + " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", + hc32_to_cpu(fotg210, itd->hw_transaction[0]), + hc32_to_cpu(fotg210, itd->hw_transaction[1]), + hc32_to_cpu(fotg210, itd->hw_transaction[2]), + hc32_to_cpu(fotg210, itd->hw_transaction[3]), + hc32_to_cpu(fotg210, itd->hw_transaction[4]), + hc32_to_cpu(fotg210, itd->hw_transaction[5]), + hc32_to_cpu(fotg210, itd->hw_transaction[6]), + hc32_to_cpu(fotg210, itd->hw_transaction[7])); + fotg210_dbg(fotg210, + " buf: %08x %08x %08x %08x %08x %08x %08x\n", + hc32_to_cpu(fotg210, itd->hw_bufp[0]), + hc32_to_cpu(fotg210, itd->hw_bufp[1]), + hc32_to_cpu(fotg210, itd->hw_bufp[2]), + hc32_to_cpu(fotg210, itd->hw_bufp[3]), + hc32_to_cpu(fotg210, itd->hw_bufp[4]), + hc32_to_cpu(fotg210, itd->hw_bufp[5]), + hc32_to_cpu(fotg210, itd->hw_bufp[6])); + fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", + itd->index[0], itd->index[1], itd->index[2], + itd->index[3], itd->index[4], itd->index[5], + itd->index[6], itd->index[7]); +} + +static int __maybe_unused +dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +{ + return scnprintf(buf, len, + "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +} + +static int __maybe_unused +dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +{ + return scnprintf(buf, len, + "%s%sintrenable %02x%s%s%s%s%s%s", + label, label[0] ? " " : "", enable, + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", + (enable & STS_PCD) ? " PCD" : "", + (enable & STS_ERR) ? " ERR" : "", + (enable & STS_INT) ? " INT" : "" + ); +} + +static const char *const fls_strings[] = { "1024", "512", "256", "??" }; + +static int +dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) +{ + return scnprintf(buf, len, + "%s%scommand %07x %s=%d ithresh=%d%s%s%s " + "period=%s%s %s", + label, label[0] ? " " : "", command, + (command & CMD_PARK) ? " park" : "(park)", + CMD_PARK_CNT(command), + (command >> 16) & 0x3f, + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + fls_strings[(command >> 2) & 0x3], + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +} + +static int +dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) +{ + char *sig; + + /* signaling state */ + switch (status & (3 << 10)) { + case 0 << 10: + sig = "se0"; + break; + case 1 << 10: + sig = "k"; + break; /* low speed */ + case 2 << 10: + sig = "j"; + break; + default: + sig = "?"; + break; + } + + return scnprintf(buf, len, + "%s%sport:%d status %06x %d " + "sig=%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", port, status, + status>>25,/*device address */ + sig, + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", + (status & PORT_PEC) ? " PEC" : "", + (status & PORT_PE) ? " PE" : "", + (status & PORT_CSC) ? " CSC" : "", + (status & PORT_CONNECT) ? " CONNECT" : ""); +} + +#else +static inline void __maybe_unused +dbg_qh(char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{} + +static inline int __maybe_unused +dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +{ return 0; } + +static inline int __maybe_unused +dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) +{ return 0; } + +static inline int __maybe_unused +dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +{ return 0; } + +static inline int __maybe_unused +dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) +{ return 0; } + +#endif /* DEBUG */ + +/* functions have the "wrong" filename when they're output... */ +#define dbg_status(fotg210, label, status) { \ + char _buf[80]; \ + dbg_status_buf(_buf, sizeof(_buf), label, status); \ + fotg210_dbg(fotg210, "%s\n", _buf); \ +} + +#define dbg_cmd(fotg210, label, command) { \ + char _buf[80]; \ + dbg_command_buf(_buf, sizeof(_buf), label, command); \ + fotg210_dbg(fotg210, "%s\n", _buf); \ +} + +#define dbg_port(fotg210, label, port, status) { \ + char _buf[80]; \ + dbg_port_buf(_buf, sizeof(_buf), label, port, status); \ + fotg210_dbg(fotg210, "%s\n", _buf); \ +} + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILES + +static inline void create_debug_files(struct fotg210_hcd *bus) { } +static inline void remove_debug_files(struct fotg210_hcd *bus) { } + +#else + +/* troubleshooting help: expose state in debugfs */ + +static int debug_async_open(struct inode *, struct file *); +static int debug_periodic_open(struct inode *, struct file *); +static int debug_registers_open(struct inode *, struct file *); +static int debug_async_open(struct inode *, struct file *); + +static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); +static int debug_close(struct inode *, struct file *); + +static const struct file_operations debug_async_fops = { + .owner = THIS_MODULE, + .open = debug_async_open, + .read = debug_output, + .release = debug_close, + .llseek = default_llseek, +}; +static const struct file_operations debug_periodic_fops = { + .owner = THIS_MODULE, + .open = debug_periodic_open, + .read = debug_output, + .release = debug_close, + .llseek = default_llseek, +}; +static const struct file_operations debug_registers_fops = { + .owner = THIS_MODULE, + .open = debug_registers_open, + .read = debug_output, + .release = debug_close, + .llseek = default_llseek, +}; + +static struct dentry *fotg210_debug_root; + +struct debug_buffer { + ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ + struct usb_bus *bus; + struct mutex mutex; /* protect filling of buffer */ + size_t count; /* number of characters filled into buffer */ + char *output_buf; + size_t alloc_size; +}; + +#define speed_char(info1)({ char tmp; \ + switch (info1 & (3 << 12)) { \ + case QH_FULL_SPEED: \ + tmp = 'f'; break; \ + case QH_LOW_SPEED: \ + tmp = 'l'; break; \ + case QH_HIGH_SPEED: \ + tmp = 'h'; break; \ + default: \ + tmp = '?'; break; \ + }; tmp; }) + +static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) +{ + __u32 v = hc32_to_cpu(fotg210, token); + + if (v & QTD_STS_ACTIVE) + return '*'; + if (v & QTD_STS_HALT) + return '-'; + if (!IS_SHORT_READ(v)) + return ' '; + /* tries to advance through hw_alt_next */ + return '/'; +} + +static void qh_lines( + struct fotg210_hcd *fotg210, + struct fotg210_qh *qh, + char **nextp, + unsigned *sizep +) +{ + u32 scratch; + u32 hw_curr; + struct fotg210_qtd *td; + unsigned temp; + unsigned size = *sizep; + char *next = *nextp; + char mark; + __le32 list_end = FOTG210_LIST_END(fotg210); + struct fotg210_qh_hw *hw = qh->hw; + + if (hw->hw_qtd_next == list_end) /* NEC does this */ + mark = '@'; + else + mark = token_mark(fotg210, hw->hw_token); + if (mark == '/') { /* qh_alt_next controls qh advance? */ + if ((hw->hw_alt_next & QTD_MASK(fotg210)) + == fotg210->async->hw->hw_alt_next) + mark = '#'; /* blocked */ + else if (hw->hw_alt_next == list_end) + mark = '.'; /* use hw_qtd_next */ + /* else alt_next points to some other qtd */ + } + scratch = hc32_to_cpup(fotg210, &hw->hw_info1); + hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0; + temp = scnprintf(next, size, + "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)", + qh, scratch & 0x007f, + speed_char(scratch), + (scratch >> 8) & 0x000f, + scratch, hc32_to_cpup(fotg210, &hw->hw_info2), + hc32_to_cpup(fotg210, &hw->hw_token), mark, + (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token) + ? "data1" : "data0", + (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f); + size -= temp; + next += temp; + + /* hc may be modifying the list as we read it ... */ + list_for_each_entry(td, &qh->qtd_list, qtd_list) { + scratch = hc32_to_cpup(fotg210, &td->hw_token); + mark = ' '; + if (hw_curr == td->qtd_dma) + mark = '*'; + else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma)) + mark = '+'; + else if (QTD_LENGTH(scratch)) { + if (td->hw_alt_next == fotg210->async->hw->hw_alt_next) + mark = '#'; + else if (td->hw_alt_next != list_end) + mark = '/'; + } + temp = snprintf(next, size, + "\n\t%p%c%s len=%d %08x urb %p", + td, mark, ({ char *tmp; + switch ((scratch>>8)&0x03) { + case 0: + tmp = "out"; + break; + case 1: + tmp = "in"; + break; + case 2: + tmp = "setup"; + break; + default: + tmp = "?"; + break; + } tmp; }), + (scratch >> 16) & 0x7fff, + scratch, + td->urb); + if (size < temp) + temp = size; + size -= temp; + next += temp; + if (temp == size) + goto done; + } + + temp = snprintf(next, size, "\n"); + if (size < temp) + temp = size; + size -= temp; + next += temp; + +done: + *sizep = size; + *nextp = next; +} + +static ssize_t fill_async_buffer(struct debug_buffer *buf) +{ + struct usb_hcd *hcd; + struct fotg210_hcd *fotg210; + unsigned long flags; + unsigned temp, size; + char *next; + struct fotg210_qh *qh; + + hcd = bus_to_hcd(buf->bus); + fotg210 = hcd_to_fotg210(hcd); + next = buf->output_buf; + size = buf->alloc_size; + + *next = 0; + + /* dumps a snapshot of the async schedule. + * usually empty except for long-term bulk reads, or head. + * one QH per line, and TDs we know about + */ + spin_lock_irqsave(&fotg210->lock, flags); + for (qh = fotg210->async->qh_next.qh; size > 0 && qh; + qh = qh->qh_next.qh) + qh_lines(fotg210, qh, &next, &size); + if (fotg210->async_unlink && size > 0) { + temp = scnprintf(next, size, "\nunlink =\n"); + size -= temp; + next += temp; + + for (qh = fotg210->async_unlink; size > 0 && qh; + qh = qh->unlink_next) + qh_lines(fotg210, qh, &next, &size); + } + spin_unlock_irqrestore(&fotg210->lock, flags); + + return strlen(buf->output_buf); +} + +#define DBG_SCHED_LIMIT 64 +static ssize_t fill_periodic_buffer(struct debug_buffer *buf) +{ + struct usb_hcd *hcd; + struct fotg210_hcd *fotg210; + unsigned long flags; + union fotg210_shadow p, *seen; + unsigned temp, size, seen_count; + char *next; + unsigned i; + __hc32 tag; + + seen = kmalloc(DBG_SCHED_LIMIT * sizeof(*seen), GFP_ATOMIC); + if (!seen) + return 0; + seen_count = 0; + + hcd = bus_to_hcd(buf->bus); + fotg210 = hcd_to_fotg210(hcd); + next = buf->output_buf; + size = buf->alloc_size; + + temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size); + size -= temp; + next += temp; + + /* dump a snapshot of the periodic schedule. + * iso changes, interrupt usually doesn't. + */ + spin_lock_irqsave(&fotg210->lock, flags); + for (i = 0; i < fotg210->periodic_size; i++) { + p = fotg210->pshadow[i]; + if (likely(!p.ptr)) + continue; + tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]); + + temp = scnprintf(next, size, "%4d: ", i); + size -= temp; + next += temp; + + do { + struct fotg210_qh_hw *hw; + + switch (hc32_to_cpu(fotg210, tag)) { + case Q_TYPE_QH: + hw = p.qh->hw; + temp = scnprintf(next, size, " qh%d-%04x/%p", + p.qh->period, + hc32_to_cpup(fotg210, + &hw->hw_info2) + /* uframe masks */ + & (QH_CMASK | QH_SMASK), + p.qh); + size -= temp; + next += temp; + /* don't repeat what follows this qh */ + for (temp = 0; temp < seen_count; temp++) { + if (seen[temp].ptr != p.ptr) + continue; + if (p.qh->qh_next.ptr) { + temp = scnprintf(next, size, + " ..."); + size -= temp; + next += temp; + } + break; + } + /* show more info the first time around */ + if (temp == seen_count) { + u32 scratch = hc32_to_cpup(fotg210, + &hw->hw_info1); + struct fotg210_qtd *qtd; + char *type = ""; + + /* count tds, get ep direction */ + temp = 0; + list_for_each_entry(qtd, + &p.qh->qtd_list, + qtd_list) { + temp++; + switch (0x03 & (hc32_to_cpu( + fotg210, + qtd->hw_token) >> 8)) { + case 0: + type = "out"; + continue; + case 1: + type = "in"; + continue; + } + } + + temp = scnprintf(next, size, + "(%c%d ep%d%s " + "[%d/%d] q%d p%d)", + speed_char(scratch), + scratch & 0x007f, + (scratch >> 8) & 0x000f, type, + p.qh->usecs, p.qh->c_usecs, + temp, + 0x7ff & (scratch >> 16)); + + if (seen_count < DBG_SCHED_LIMIT) + seen[seen_count++].qh = p.qh; + } else + temp = 0; + tag = Q_NEXT_TYPE(fotg210, hw->hw_next); + p = p.qh->qh_next; + break; + case Q_TYPE_FSTN: + temp = scnprintf(next, size, + " fstn-%8x/%p", p.fstn->hw_prev, + p.fstn); + tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next); + p = p.fstn->fstn_next; + break; + case Q_TYPE_ITD: + temp = scnprintf(next, size, + " itd/%p", p.itd); + tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); + p = p.itd->itd_next; + break; + } + size -= temp; + next += temp; + } while (p.ptr); + + temp = scnprintf(next, size, "\n"); + size -= temp; + next += temp; + } + spin_unlock_irqrestore(&fotg210->lock, flags); + kfree(seen); + + return buf->alloc_size - size; +} +#undef DBG_SCHED_LIMIT + +static const char *rh_state_string(struct fotg210_hcd *fotg210) +{ + switch (fotg210->rh_state) { + case FOTG210_RH_HALTED: + return "halted"; + case FOTG210_RH_SUSPENDED: + return "suspended"; + case FOTG210_RH_RUNNING: + return "running"; + case FOTG210_RH_STOPPING: + return "stopping"; + } + return "?"; +} + +static ssize_t fill_registers_buffer(struct debug_buffer *buf) +{ + struct usb_hcd *hcd; + struct fotg210_hcd *fotg210; + unsigned long flags; + unsigned temp, size, i; + char *next, scratch[80]; + static const char fmt[] = "%*s\n"; + static const char label[] = ""; + + hcd = bus_to_hcd(buf->bus); + fotg210 = hcd_to_fotg210(hcd); + next = buf->output_buf; + size = buf->alloc_size; + + spin_lock_irqsave(&fotg210->lock, flags); + + if (!HCD_HW_ACCESSIBLE(hcd)) { + size = scnprintf(next, size, + "bus %s, device %s\n" + "%s\n" + "SUSPENDED(no register access)\n", + hcd->self.controller->bus->name, + dev_name(hcd->self.controller), + hcd->product_desc); + goto done; + } + + /* Capability Registers */ + i = HC_VERSION(fotg210, fotg210_readl(fotg210, + &fotg210->caps->hc_capbase)); + temp = scnprintf(next, size, + "bus %s, device %s\n" + "%s\n" + "EHCI %x.%02x, rh state %s\n", + hcd->self.controller->bus->name, + dev_name(hcd->self.controller), + hcd->product_desc, + i >> 8, i & 0x0ff, rh_state_string(fotg210)); + size -= temp; + next += temp; + + /* FIXME interpret both types of params */ + i = fotg210_readl(fotg210, &fotg210->caps->hcs_params); + temp = scnprintf(next, size, "structural params 0x%08x\n", i); + size -= temp; + next += temp; + + i = fotg210_readl(fotg210, &fotg210->caps->hcc_params); + temp = scnprintf(next, size, "capability params 0x%08x\n", i); + size -= temp; + next += temp; + + /* Operational Registers */ + temp = dbg_status_buf(scratch, sizeof(scratch), label, + fotg210_readl(fotg210, &fotg210->regs->status)); + temp = scnprintf(next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_command_buf(scratch, sizeof(scratch), label, + fotg210_readl(fotg210, &fotg210->regs->command)); + temp = scnprintf(next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_intr_buf(scratch, sizeof(scratch), label, + fotg210_readl(fotg210, &fotg210->regs->intr_enable)); + temp = scnprintf(next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = scnprintf(next, size, "uframe %04x\n", + fotg210_read_frame_index(fotg210)); + size -= temp; + next += temp; + + if (fotg210->async_unlink) { + temp = scnprintf(next, size, "async unlink qh %p\n", + fotg210->async_unlink); + size -= temp; + next += temp; + } + +#ifdef FOTG210_STATS + temp = scnprintf(next, size, + "irq normal %ld err %ld iaa %ld(lost %ld)\n", + fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa, + fotg210->stats.lost_iaa); + size -= temp; + next += temp; + + temp = scnprintf(next, size, "complete %ld unlink %ld\n", + fotg210->stats.complete, fotg210->stats.unlink); + size -= temp; + next += temp; +#endif + +done: + spin_unlock_irqrestore(&fotg210->lock, flags); + + return buf->alloc_size - size; +} + +static struct debug_buffer *alloc_buffer(struct usb_bus *bus, + ssize_t (*fill_func)(struct debug_buffer *)) +{ + struct debug_buffer *buf; + + buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); + + if (buf) { + buf->bus = bus; + buf->fill_func = fill_func; + mutex_init(&buf->mutex); + buf->alloc_size = PAGE_SIZE; + } + + return buf; +} + +static int fill_buffer(struct debug_buffer *buf) +{ + int ret = 0; + + if (!buf->output_buf) + buf->output_buf = vmalloc(buf->alloc_size); + + if (!buf->output_buf) { + ret = -ENOMEM; + goto out; + } + + ret = buf->fill_func(buf); + + if (ret >= 0) { + buf->count = ret; + ret = 0; + } + +out: + return ret; +} + +static ssize_t debug_output(struct file *file, char __user *user_buf, + size_t len, loff_t *offset) +{ + struct debug_buffer *buf = file->private_data; + int ret = 0; + + mutex_lock(&buf->mutex); + if (buf->count == 0) { + ret = fill_buffer(buf); + if (ret != 0) { + mutex_unlock(&buf->mutex); + goto out; + } + } + mutex_unlock(&buf->mutex); + + ret = simple_read_from_buffer(user_buf, len, offset, + buf->output_buf, buf->count); + +out: + return ret; + +} + +static int debug_close(struct inode *inode, struct file *file) +{ + struct debug_buffer *buf = file->private_data; + + if (buf) { + vfree(buf->output_buf); + kfree(buf); + } + + return 0; +} +static int debug_async_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + +static int debug_periodic_open(struct inode *inode, struct file *file) +{ + struct debug_buffer *buf; + buf = alloc_buffer(inode->i_private, fill_periodic_buffer); + if (!buf) + return -ENOMEM; + + buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; + file->private_data = buf; + return 0; +} + +static int debug_registers_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, + fill_registers_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + +static inline void create_debug_files(struct fotg210_hcd *fotg210) +{ + struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; + + fotg210->debug_dir = debugfs_create_dir(bus->bus_name, + fotg210_debug_root); + if (!fotg210->debug_dir) + return; + + if (!debugfs_create_file("async", S_IRUGO, fotg210->debug_dir, bus, + &debug_async_fops)) + goto file_error; + + if (!debugfs_create_file("periodic", S_IRUGO, fotg210->debug_dir, bus, + &debug_periodic_fops)) + goto file_error; + + if (!debugfs_create_file("registers", S_IRUGO, fotg210->debug_dir, bus, + &debug_registers_fops)) + goto file_error; + + return; + +file_error: + debugfs_remove_recursive(fotg210->debug_dir); +} + +static inline void remove_debug_files(struct fotg210_hcd *fotg210) +{ + debugfs_remove_recursive(fotg210->debug_dir); +} + +#endif /* STUB_DEBUG_FILES */ +/*-------------------------------------------------------------------------*/ + +/* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, + u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = fotg210_readl(fotg210, ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +/* + * Force HC to halt state from unknown (EHCI spec section 2.3). + * Must be called with interrupts enabled and the lock not held. + */ +static int fotg210_halt(struct fotg210_hcd *fotg210) +{ + u32 temp; + + spin_lock_irq(&fotg210->lock); + + /* disable any irqs left enabled by previous code */ + fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); + + /* + * This routine gets called during probe before fotg210->command + * has been initialized, so we can't rely on its value. + */ + fotg210->command &= ~CMD_RUN; + temp = fotg210_readl(fotg210, &fotg210->regs->command); + temp &= ~(CMD_RUN | CMD_IAAD); + fotg210_writel(fotg210, temp, &fotg210->regs->command); + + spin_unlock_irq(&fotg210->lock); + synchronize_irq(fotg210_to_hcd(fotg210)->irq); + + return handshake(fotg210, &fotg210->regs->status, + STS_HALT, STS_HALT, 16 * 125); +} + +/* + * Reset a non-running (STS_HALT == 1) controller. + * Must be called with interrupts enabled and the lock not held. + */ +static int fotg210_reset(struct fotg210_hcd *fotg210) +{ + int retval; + u32 command = fotg210_readl(fotg210, &fotg210->regs->command); + + /* If the EHCI debug controller is active, special care must be + * taken before and after a host controller reset */ + if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210))) + fotg210->debug = NULL; + + command |= CMD_RESET; + dbg_cmd(fotg210, "reset", command); + fotg210_writel(fotg210, command, &fotg210->regs->command); + fotg210->rh_state = FOTG210_RH_HALTED; + fotg210->next_statechange = jiffies; + retval = handshake(fotg210, &fotg210->regs->command, + CMD_RESET, 0, 250 * 1000); + + if (retval) + return retval; + + if (fotg210->debug) + dbgp_external_startup(fotg210_to_hcd(fotg210)); + + fotg210->port_c_suspend = fotg210->suspended_ports = + fotg210->resuming_ports = 0; + return retval; +} + +/* + * Idle the controller (turn off the schedules). + * Must be called with interrupts enabled and the lock not held. + */ +static void fotg210_quiesce(struct fotg210_hcd *fotg210) +{ + u32 temp; + + if (fotg210->rh_state != FOTG210_RH_RUNNING) + return; + + /* wait for any schedule enables/disables to take effect */ + temp = (fotg210->command << 10) & (STS_ASS | STS_PSS); + handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp, + 16 * 125); + + /* then disable anything that's still active */ + spin_lock_irq(&fotg210->lock); + fotg210->command &= ~(CMD_ASE | CMD_PSE); + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + spin_unlock_irq(&fotg210->lock); + + /* hardware can take 16 microframes to turn off ... */ + handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0, + 16 * 125); +} + +/*-------------------------------------------------------------------------*/ + +static void end_unlink_async(struct fotg210_hcd *fotg210); +static void unlink_empty_async(struct fotg210_hcd *fotg210); +static void fotg210_work(struct fotg210_hcd *fotg210); +static void start_unlink_intr(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh); +static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); + +/*-------------------------------------------------------------------------*/ + +/* Set a bit in the USBCMD register */ +static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) +{ + fotg210->command |= bit; + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + + /* unblock posted write */ + fotg210_readl(fotg210, &fotg210->regs->command); +} + +/* Clear a bit in the USBCMD register */ +static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) +{ + fotg210->command &= ~bit; + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + + /* unblock posted write */ + fotg210_readl(fotg210, &fotg210->regs->command); +} + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI timer support... Now using hrtimers. + * + * Lots of different events are triggered from fotg210->hrtimer. Whenever + * the timer routine runs, it checks each possible event; events that are + * currently enabled and whose expiration time has passed get handled. + * The set of enabled events is stored as a collection of bitflags in + * fotg210->enabled_hrtimer_events, and they are numbered in order of + * increasing delay values (ranging between 1 ms and 100 ms). + * + * Rather than implementing a sorted list or tree of all pending events, + * we keep track only of the lowest-numbered pending event, in + * fotg210->next_hrtimer_event. Whenever fotg210->hrtimer gets restarted, its + * expiration time is set to the timeout value for this event. + * + * As a result, events might not get handled right away; the actual delay + * could be anywhere up to twice the requested delay. This doesn't + * matter, because none of the events are especially time-critical. The + * ones that matter most all have a delay of 1 ms, so they will be + * handled after 2 ms at most, which is okay. In addition to this, we + * allow for an expiration range of 1 ms. + */ + +/* + * Delay lengths for the hrtimer event types. + * Keep this list sorted by delay length, in the same order as + * the event types indexed by enum fotg210_hrtimer_event in fotg210.h. + */ +static unsigned event_delays_ns[] = { + 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */ + 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */ + 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */ + 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */ + 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */ + 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ + 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */ + 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ + 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */ + 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */ +}; + +/* Enable a pending hrtimer event */ +static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, + bool resched) +{ + ktime_t *timeout = &fotg210->hr_timeouts[event]; + + if (resched) + *timeout = ktime_add(ktime_get(), + ktime_set(0, event_delays_ns[event])); + fotg210->enabled_hrtimer_events |= (1 << event); + + /* Track only the lowest-numbered pending event */ + if (event < fotg210->next_hrtimer_event) { + fotg210->next_hrtimer_event = event; + hrtimer_start_range_ns(&fotg210->hrtimer, *timeout, + NSEC_PER_MSEC, HRTIMER_MODE_ABS); + } +} + + +/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ +static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) +{ + unsigned actual, want; + + /* Don't enable anything if the controller isn't running (e.g., died) */ + if (fotg210->rh_state != FOTG210_RH_RUNNING) + return; + + want = (fotg210->command & CMD_ASE) ? STS_ASS : 0; + actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS; + + if (want != actual) { + + /* Poll again later, but give up after about 20 ms */ + if (fotg210->ASS_poll_count++ < 20) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS, + true); + return; + } + fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n", + want, actual); + } + fotg210->ASS_poll_count = 0; + + /* The status is up-to-date; restart or stop the schedule as needed */ + if (want == 0) { /* Stopped */ + if (fotg210->async_count > 0) + fotg210_set_command_bit(fotg210, CMD_ASE); + + } else { /* Running */ + if (fotg210->async_count == 0) { + + /* Turn off the schedule after a while */ + fotg210_enable_event(fotg210, + FOTG210_HRTIMER_DISABLE_ASYNC, + true); + } + } +} + +/* Turn off the async schedule after a brief delay */ +static void fotg210_disable_ASE(struct fotg210_hcd *fotg210) +{ + fotg210_clear_command_bit(fotg210, CMD_ASE); +} + + +/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ +static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) +{ + unsigned actual, want; + + /* Don't do anything if the controller isn't running (e.g., died) */ + if (fotg210->rh_state != FOTG210_RH_RUNNING) + return; + + want = (fotg210->command & CMD_PSE) ? STS_PSS : 0; + actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS; + + if (want != actual) { + + /* Poll again later, but give up after about 20 ms */ + if (fotg210->PSS_poll_count++ < 20) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS, + true); + return; + } + fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n", + want, actual); + } + fotg210->PSS_poll_count = 0; + + /* The status is up-to-date; restart or stop the schedule as needed */ + if (want == 0) { /* Stopped */ + if (fotg210->periodic_count > 0) + fotg210_set_command_bit(fotg210, CMD_PSE); + + } else { /* Running */ + if (fotg210->periodic_count == 0) { + + /* Turn off the schedule after a while */ + fotg210_enable_event(fotg210, + FOTG210_HRTIMER_DISABLE_PERIODIC, + true); + } + } +} + +/* Turn off the periodic schedule after a brief delay */ +static void fotg210_disable_PSE(struct fotg210_hcd *fotg210) +{ + fotg210_clear_command_bit(fotg210, CMD_PSE); +} + + +/* Poll the STS_HALT status bit; see when a dead controller stops */ +static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) +{ + if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) { + + /* Give up after a few milliseconds */ + if (fotg210->died_poll_count++ < 5) { + /* Try again later */ + fotg210_enable_event(fotg210, + FOTG210_HRTIMER_POLL_DEAD, true); + return; + } + fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n"); + } + + /* Clean up the mess */ + fotg210->rh_state = FOTG210_RH_HALTED; + fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); + fotg210_work(fotg210); + end_unlink_async(fotg210); + + /* Not in process context, so don't try to reset the controller */ +} + + +/* Handle unlinked interrupt QHs once they are gone from the hardware */ +static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) +{ + bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); + + /* + * Process all the QHs on the intr_unlink list that were added + * before the current unlink cycle began. The list is in + * temporal order, so stop when we reach the first entry in the + * current cycle. But if the root hub isn't running then + * process all the QHs on the list. + */ + fotg210->intr_unlinking = true; + while (fotg210->intr_unlink) { + struct fotg210_qh *qh = fotg210->intr_unlink; + + if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle) + break; + fotg210->intr_unlink = qh->unlink_next; + qh->unlink_next = NULL; + end_unlink_intr(fotg210, qh); + } + + /* Handle remaining entries later */ + if (fotg210->intr_unlink) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, + true); + ++fotg210->intr_unlink_cycle; + } + fotg210->intr_unlinking = false; +} + + +/* Start another free-iTDs/siTDs cycle */ +static void start_free_itds(struct fotg210_hcd *fotg210) +{ + if (!(fotg210->enabled_hrtimer_events & + BIT(FOTG210_HRTIMER_FREE_ITDS))) { + fotg210->last_itd_to_free = list_entry( + fotg210->cached_itd_list.prev, + struct fotg210_itd, itd_list); + fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true); + } +} + +/* Wait for controller to stop using old iTDs and siTDs */ +static void end_free_itds(struct fotg210_hcd *fotg210) +{ + struct fotg210_itd *itd, *n; + + if (fotg210->rh_state < FOTG210_RH_RUNNING) + fotg210->last_itd_to_free = NULL; + + list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) { + list_del(&itd->itd_list); + dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma); + if (itd == fotg210->last_itd_to_free) + break; + } + + if (!list_empty(&fotg210->cached_itd_list)) + start_free_itds(fotg210); +} + + +/* Handle lost (or very late) IAA interrupts */ +static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) +{ + if (fotg210->rh_state != FOTG210_RH_RUNNING) + return; + + /* + * Lost IAA irqs wedge things badly; seen first with a vt8235. + * So we need this watchdog, but must protect it against both + * (a) SMP races against real IAA firing and retriggering, and + * (b) clean HC shutdown, when IAA watchdog was pending. + */ + if (fotg210->async_iaa) { + u32 cmd, status; + + /* If we get here, IAA is *REALLY* late. It's barely + * conceivable that the system is so busy that CMD_IAAD + * is still legitimately set, so let's be sure it's + * clear before we read STS_IAA. (The HC should clear + * CMD_IAAD when it sets STS_IAA.) + */ + cmd = fotg210_readl(fotg210, &fotg210->regs->command); + + /* + * If IAA is set here it either legitimately triggered + * after the watchdog timer expired (_way_ late, so we'll + * still count it as lost) ... or a silicon erratum: + * - VIA seems to set IAA without triggering the IRQ; + * - IAAD potentially cleared without setting IAA. + */ + status = fotg210_readl(fotg210, &fotg210->regs->status); + if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { + COUNT(fotg210->stats.lost_iaa); + fotg210_writel(fotg210, STS_IAA, + &fotg210->regs->status); + } + + fotg210_vdbg(fotg210, "IAA watchdog: status %x cmd %x\n", + status, cmd); + end_unlink_async(fotg210); + } +} + + +/* Enable the I/O watchdog, if appropriate */ +static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) +{ + /* Not needed if the controller isn't running or it's already enabled */ + if (fotg210->rh_state != FOTG210_RH_RUNNING || + (fotg210->enabled_hrtimer_events & + BIT(FOTG210_HRTIMER_IO_WATCHDOG))) + return; + + /* + * Isochronous transfers always need the watchdog. + * For other sorts we use it only if the flag is set. + */ + if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog && + fotg210->async_count + fotg210->intr_count > 0)) + fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG, + true); +} + + +/* + * Handler functions for the hrtimer event types. + * Keep this array in the same order as the event types indexed by + * enum fotg210_hrtimer_event in fotg210.h. + */ +static void (*event_handlers[])(struct fotg210_hcd *) = { + fotg210_poll_ASS, /* FOTG210_HRTIMER_POLL_ASS */ + fotg210_poll_PSS, /* FOTG210_HRTIMER_POLL_PSS */ + fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */ + fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */ + end_free_itds, /* FOTG210_HRTIMER_FREE_ITDS */ + unlink_empty_async, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ + fotg210_iaa_watchdog, /* FOTG210_HRTIMER_IAA_WATCHDOG */ + fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ + fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */ + fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */ +}; + +static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) +{ + struct fotg210_hcd *fotg210 = + container_of(t, struct fotg210_hcd, hrtimer); + ktime_t now; + unsigned long events; + unsigned long flags; + unsigned e; + + spin_lock_irqsave(&fotg210->lock, flags); + + events = fotg210->enabled_hrtimer_events; + fotg210->enabled_hrtimer_events = 0; + fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; + + /* + * Check each pending event. If its time has expired, handle + * the event; otherwise re-enable it. + */ + now = ktime_get(); + for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) { + if (now.tv64 >= fotg210->hr_timeouts[e].tv64) + event_handlers[e](fotg210); + else + fotg210_enable_event(fotg210, e, false); + } + + spin_unlock_irqrestore(&fotg210->lock, flags); + return HRTIMER_NORESTART; +} + +/*-------------------------------------------------------------------------*/ + +#define fotg210_bus_suspend NULL +#define fotg210_bus_resume NULL + +/*-------------------------------------------------------------------------*/ + +static int check_reset_complete( + struct fotg210_hcd *fotg210, + int index, + u32 __iomem *status_reg, + int port_status +) { + if (!(port_status & PORT_CONNECT)) + return port_status; + + /* if reset finished and it's still not enabled -- handoff */ + if (!(port_status & PORT_PE)) { + /* with integrated TT, there's nobody to hand it to! */ + fotg210_dbg(fotg210, + "Failed to enable port %d on root hub TT\n", + index+1); + return port_status; + } else { + fotg210_dbg(fotg210, "port %d reset complete, port enabled\n", + index + 1); + } + + return port_status; +} + +/*-------------------------------------------------------------------------*/ + + +/* build "status change" packet (one or two bytes) from HC registers */ + +static int +fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp, status; + u32 mask; + int retval = 1; + unsigned long flags; + + /* init status to no-changes */ + buf[0] = 0; + + /* Inform the core about resumes-in-progress by returning + * a non-zero value even if there are no status changes. + */ + status = fotg210->resuming_ports; + + mask = PORT_CSC | PORT_PEC; + /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */ + + /* no hub change reports (bit 0) for now (power, ...) */ + + /* port N changes (bit N)? */ + spin_lock_irqsave(&fotg210->lock, flags); + + temp = fotg210_readl(fotg210, &fotg210->regs->port_status); + + /* + * Return status information even for ports with OWNER set. + * Otherwise khubd wouldn't see the disconnect event when a + * high-speed device is switched over to the companion + * controller by the user. + */ + + if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) + || (fotg210->reset_done[0] && time_after_eq( + jiffies, fotg210->reset_done[0]))) { + buf[0] |= 1 << 1; + status = STS_PCD; + } + /* FIXME autosuspend idle root hubs */ + spin_unlock_irqrestore(&fotg210->lock, flags); + return status ? retval : 0; +} + +/*-------------------------------------------------------------------------*/ + +static void +fotg210_hub_descriptor( + struct fotg210_hcd *fotg210, + struct usb_hub_descriptor *desc +) { + int ports = HCS_N_PORTS(fotg210->hcs_params); + u16 temp; + + desc->bDescriptorType = 0x29; + desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */ + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = ports; + temp = 1 + (ports / 8); + desc->bDescLength = 7 + 2 * temp; + + /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ + memset(&desc->u.hs.DeviceRemovable[0], 0, temp); + memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); + + temp = 0x0008; /* per-port overcurrent reporting */ + temp |= 0x0002; /* no power switching */ + desc->wHubCharacteristics = cpu_to_le16(temp); +} + +/*-------------------------------------------------------------------------*/ + +static int fotg210_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) { + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + int ports = HCS_N_PORTS(fotg210->hcs_params); + u32 __iomem *status_reg = &fotg210->regs->port_status; + u32 temp, temp1, status; + unsigned long flags; + int retval = 0; + unsigned selector; + + /* + * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. + * HCS_INDICATOR may say we can change LEDs to off/amber/green. + * (track current state ourselves) ... blink for diagnostics, + * power, "this is the one", etc. EHCI spec supports this. + */ + + spin_lock_irqsave(&fotg210->lock, flags); + switch (typeReq) { + case ClearHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = fotg210_readl(fotg210, status_reg); + temp &= ~PORT_RWC_BITS; + + /* + * Even if OWNER is set, so the port is owned by the + * companion controller, khubd needs to be able to clear + * the port-change status bits (especially + * USB_PORT_STAT_C_CONNECTION). + */ + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + fotg210_writel(fotg210, temp & ~PORT_PE, status_reg); + break; + case USB_PORT_FEAT_C_ENABLE: + fotg210_writel(fotg210, temp | PORT_PEC, status_reg); + break; + case USB_PORT_FEAT_SUSPEND: + if (temp & PORT_RESET) + goto error; + if (!(temp & PORT_SUSPEND)) + break; + if ((temp & PORT_PE) == 0) + goto error; + + /* resume signaling for 20 msec */ + fotg210_writel(fotg210, temp | PORT_RESUME, status_reg); + fotg210->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + break; + case USB_PORT_FEAT_C_SUSPEND: + clear_bit(wIndex, &fotg210->port_c_suspend); + break; + case USB_PORT_FEAT_C_CONNECTION: + fotg210_writel(fotg210, temp | PORT_CSC, status_reg); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + fotg210_writel(fotg210, temp | OTGISR_OVC, + &fotg210->regs->otgisr); + break; + case USB_PORT_FEAT_C_RESET: + /* GetPortStatus clears reset */ + break; + default: + goto error; + } + fotg210_readl(fotg210, &fotg210->regs->command); + break; + case GetHubDescriptor: + fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *) + buf); + break; + case GetHubStatus: + /* no hub-wide feature/status flags */ + memset(buf, 0, 4); + /*cpu_to_le32s ((u32 *) buf); */ + break; + case GetPortStatus: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + status = 0; + temp = fotg210_readl(fotg210, status_reg); + + /* wPortChange bits */ + if (temp & PORT_CSC) + status |= USB_PORT_STAT_C_CONNECTION << 16; + if (temp & PORT_PEC) + status |= USB_PORT_STAT_C_ENABLE << 16; + + temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); + if (temp1 & OTGISR_OVC) + status |= USB_PORT_STAT_C_OVERCURRENT << 16; + + /* whoever resumes must GetPortStatus to complete it!! */ + if (temp & PORT_RESUME) { + + /* Remote Wakeup received? */ + if (!fotg210->reset_done[wIndex]) { + /* resume signaling for 20 msec */ + fotg210->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + /* check the port again */ + mod_timer(&fotg210_to_hcd(fotg210)->rh_timer, + fotg210->reset_done[wIndex]); + } + + /* resume completed? */ + else if (time_after_eq(jiffies, + fotg210->reset_done[wIndex])) { + clear_bit(wIndex, &fotg210->suspended_ports); + set_bit(wIndex, &fotg210->port_c_suspend); + fotg210->reset_done[wIndex] = 0; + + /* stop resume signaling */ + temp = fotg210_readl(fotg210, status_reg); + fotg210_writel(fotg210, + temp & ~(PORT_RWC_BITS | PORT_RESUME), + status_reg); + clear_bit(wIndex, &fotg210->resuming_ports); + retval = handshake(fotg210, status_reg, + PORT_RESUME, 0, 2000 /* 2msec */); + if (retval != 0) { + fotg210_err(fotg210, + "port %d resume error %d\n", + wIndex + 1, retval); + goto error; + } + temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); + } + } + + /* whoever resets must GetPortStatus to complete it!! */ + if ((temp & PORT_RESET) + && time_after_eq(jiffies, + fotg210->reset_done[wIndex])) { + status |= USB_PORT_STAT_C_RESET << 16; + fotg210->reset_done[wIndex] = 0; + clear_bit(wIndex, &fotg210->resuming_ports); + + /* force reset to complete */ + fotg210_writel(fotg210, + temp & ~(PORT_RWC_BITS | PORT_RESET), + status_reg); + /* REVISIT: some hardware needs 550+ usec to clear + * this bit; seems too long to spin routinely... + */ + retval = handshake(fotg210, status_reg, + PORT_RESET, 0, 1000); + if (retval != 0) { + fotg210_err(fotg210, "port %d reset error %d\n", + wIndex + 1, retval); + goto error; + } + + /* see what we found out */ + temp = check_reset_complete(fotg210, wIndex, status_reg, + fotg210_readl(fotg210, status_reg)); + } + + if (!(temp & (PORT_RESUME|PORT_RESET))) { + fotg210->reset_done[wIndex] = 0; + clear_bit(wIndex, &fotg210->resuming_ports); + } + + /* transfer dedicated ports to the companion hc */ + if ((temp & PORT_CONNECT) && + test_bit(wIndex, &fotg210->companion_ports)) { + temp &= ~PORT_RWC_BITS; + fotg210_writel(fotg210, temp, status_reg); + fotg210_dbg(fotg210, "port %d --> companion\n", + wIndex + 1); + temp = fotg210_readl(fotg210, status_reg); + } + + /* + * Even if OWNER is set, there's no harm letting khubd + * see the wPortStatus values (they should all be 0 except + * for PORT_POWER anyway). + */ + + if (temp & PORT_CONNECT) { + status |= USB_PORT_STAT_CONNECTION; + status |= fotg210_port_speed(fotg210, temp); + } + if (temp & PORT_PE) + status |= USB_PORT_STAT_ENABLE; + + /* maybe the port was unsuspended without our knowledge */ + if (temp & (PORT_SUSPEND|PORT_RESUME)) { + status |= USB_PORT_STAT_SUSPEND; + } else if (test_bit(wIndex, &fotg210->suspended_ports)) { + clear_bit(wIndex, &fotg210->suspended_ports); + clear_bit(wIndex, &fotg210->resuming_ports); + fotg210->reset_done[wIndex] = 0; + if (temp & PORT_PE) + set_bit(wIndex, &fotg210->port_c_suspend); + } + + temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); + if (temp1 & OTGISR_OVC) + status |= USB_PORT_STAT_OVERCURRENT; + if (temp & PORT_RESET) + status |= USB_PORT_STAT_RESET; + if (test_bit(wIndex, &fotg210->port_c_suspend)) + status |= USB_PORT_STAT_C_SUSPEND << 16; + +#ifndef VERBOSE_DEBUG + if (status & ~0xffff) /* only if wPortChange is interesting */ +#endif + dbg_port(fotg210, "GetStatus", wIndex + 1, temp); + put_unaligned_le32(status, buf); + break; + case SetHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case SetPortFeature: + selector = wIndex >> 8; + wIndex &= 0xff; + + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = fotg210_readl(fotg210, status_reg); + temp &= ~PORT_RWC_BITS; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if ((temp & PORT_PE) == 0 + || (temp & PORT_RESET) != 0) + goto error; + + /* After above check the port must be connected. + * Set appropriate bit thus could put phy into low power + * mode if we have hostpc feature + */ + fotg210_writel(fotg210, temp | PORT_SUSPEND, + status_reg); + set_bit(wIndex, &fotg210->suspended_ports); + break; + case USB_PORT_FEAT_RESET: + if (temp & PORT_RESUME) + goto error; + /* line status bits may report this as low speed, + * which can be fine if this root hub has a + * transaction translator built in. + */ + fotg210_vdbg(fotg210, "port %d reset\n", wIndex + 1); + temp |= PORT_RESET; + temp &= ~PORT_PE; + + /* + * caller must wait, then call GetPortStatus + * usb 2.0 spec says 50 ms resets on root + */ + fotg210->reset_done[wIndex] = jiffies + + msecs_to_jiffies(50); + fotg210_writel(fotg210, temp, status_reg); + break; + + /* For downstream facing ports (these): one hub port is put + * into test mode according to USB2 11.24.2.13, then the hub + * must be reset (which for root hub now means rmmod+modprobe, + * or else system reboot). See EHCI 2.3.9 and 4.14 for info + * about the EHCI-specific stuff. + */ + case USB_PORT_FEAT_TEST: + if (!selector || selector > 5) + goto error; + spin_unlock_irqrestore(&fotg210->lock, flags); + fotg210_quiesce(fotg210); + spin_lock_irqsave(&fotg210->lock, flags); + + /* Put all enabled ports into suspend */ + temp = fotg210_readl(fotg210, status_reg) & + ~PORT_RWC_BITS; + if (temp & PORT_PE) + fotg210_writel(fotg210, temp | PORT_SUSPEND, + status_reg); + + spin_unlock_irqrestore(&fotg210->lock, flags); + fotg210_halt(fotg210); + spin_lock_irqsave(&fotg210->lock, flags); + + temp = fotg210_readl(fotg210, status_reg); + temp |= selector << 16; + fotg210_writel(fotg210, temp, status_reg); + break; + + default: + goto error; + } + fotg210_readl(fotg210, &fotg210->regs->command); + break; + + default: +error: + /* "stall" on error */ + retval = -EPIPE; + } + spin_unlock_irqrestore(&fotg210->lock, flags); + return retval; +} + +static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd, + int portnum) +{ + return; +} + +static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, + int portnum) +{ + return 0; +} +/*-------------------------------------------------------------------------*/ +/* + * There's basically three types of memory: + * - data used only by the HCD ... kmalloc is fine + * - async and periodic schedules, shared by HC and HCD ... these + * need to use dma_pool or dma_alloc_coherent + * - driver buffers, read/written by HC ... single shot DMA mapped + * + * There's also "register" data (e.g. PCI or SOC), which is memory mapped. + * No memory seen by this driver is pageable. + */ + +/*-------------------------------------------------------------------------*/ + +/* Allocate the key transfer structures from the previously allocated pool */ + +static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, + struct fotg210_qtd *qtd, dma_addr_t dma) +{ + memset(qtd, 0, sizeof(*qtd)); + qtd->qtd_dma = dma; + qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); + qtd->hw_next = FOTG210_LIST_END(fotg210); + qtd->hw_alt_next = FOTG210_LIST_END(fotg210); + INIT_LIST_HEAD(&qtd->qtd_list); +} + +static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, + gfp_t flags) +{ + struct fotg210_qtd *qtd; + dma_addr_t dma; + + qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma); + if (qtd != NULL) + fotg210_qtd_init(fotg210, qtd, dma); + + return qtd; +} + +static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210, + struct fotg210_qtd *qtd) +{ + dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma); +} + + +static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + /* clean qtds first, and know this is not linked */ + if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { + fotg210_dbg(fotg210, "unused qh not empty!\n"); + BUG(); + } + if (qh->dummy) + fotg210_qtd_free(fotg210, qh->dummy); + dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); + kfree(qh); +} + +static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, + gfp_t flags) +{ + struct fotg210_qh *qh; + dma_addr_t dma; + + qh = kzalloc(sizeof(*qh), GFP_ATOMIC); + if (!qh) + goto done; + qh->hw = (struct fotg210_qh_hw *) + dma_pool_alloc(fotg210->qh_pool, flags, &dma); + if (!qh->hw) + goto fail; + memset(qh->hw, 0, sizeof(*qh->hw)); + qh->qh_dma = dma; + INIT_LIST_HEAD(&qh->qtd_list); + + /* dummy td enables safe urb queuing */ + qh->dummy = fotg210_qtd_alloc(fotg210, flags); + if (qh->dummy == NULL) { + fotg210_dbg(fotg210, "no dummy td\n"); + goto fail1; + } +done: + return qh; +fail1: + dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); +fail: + kfree(qh); + return NULL; +} + +/*-------------------------------------------------------------------------*/ + +/* The queue heads and transfer descriptors are managed from pools tied + * to each of the "per device" structures. + * This is the initialisation and cleanup code. + */ + +static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) +{ + if (fotg210->async) + qh_destroy(fotg210, fotg210->async); + fotg210->async = NULL; + + if (fotg210->dummy) + qh_destroy(fotg210, fotg210->dummy); + fotg210->dummy = NULL; + + /* DMA consistent memory and pools */ + if (fotg210->qtd_pool) + dma_pool_destroy(fotg210->qtd_pool); + fotg210->qtd_pool = NULL; + + if (fotg210->qh_pool) { + dma_pool_destroy(fotg210->qh_pool); + fotg210->qh_pool = NULL; + } + + if (fotg210->itd_pool) + dma_pool_destroy(fotg210->itd_pool); + fotg210->itd_pool = NULL; + + if (fotg210->periodic) + dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, + fotg210->periodic_size * sizeof(u32), + fotg210->periodic, fotg210->periodic_dma); + fotg210->periodic = NULL; + + /* shadow periodic table */ + kfree(fotg210->pshadow); + fotg210->pshadow = NULL; +} + +/* remember to add cleanup code (above) if you add anything here */ +static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) +{ + int i; + + /* QTDs for control/bulk/intr transfers */ + fotg210->qtd_pool = dma_pool_create("fotg210_qtd", + fotg210_to_hcd(fotg210)->self.controller, + sizeof(struct fotg210_qtd), + 32 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */); + if (!fotg210->qtd_pool) + goto fail; + + /* QHs for control/bulk/intr transfers */ + fotg210->qh_pool = dma_pool_create("fotg210_qh", + fotg210_to_hcd(fotg210)->self.controller, + sizeof(struct fotg210_qh_hw), + 32 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */); + if (!fotg210->qh_pool) + goto fail; + + fotg210->async = fotg210_qh_alloc(fotg210, flags); + if (!fotg210->async) + goto fail; + + /* ITD for high speed ISO transfers */ + fotg210->itd_pool = dma_pool_create("fotg210_itd", + fotg210_to_hcd(fotg210)->self.controller, + sizeof(struct fotg210_itd), + 64 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */); + if (!fotg210->itd_pool) + goto fail; + + /* Hardware periodic table */ + fotg210->periodic = (__le32 *) + dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, + fotg210->periodic_size * sizeof(__le32), + &fotg210->periodic_dma, 0); + if (fotg210->periodic == NULL) + goto fail; + + for (i = 0; i < fotg210->periodic_size; i++) + fotg210->periodic[i] = FOTG210_LIST_END(fotg210); + + /* software shadow of hardware table */ + fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *), + flags); + if (fotg210->pshadow != NULL) + return 0; + +fail: + fotg210_dbg(fotg210, "couldn't init memory\n"); + fotg210_mem_cleanup(fotg210); + return -ENOMEM; +} +/*-------------------------------------------------------------------------*/ +/* + * EHCI hardware queue manipulation ... the core. QH/QTD manipulation. + * + * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" + * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned + * buffers needed for the larger number). We use one QH per endpoint, queue + * multiple urbs (all three types) per endpoint. URBs may need several qtds. + * + * ISO traffic uses "ISO TD" (itd) records, and (along with + * interrupts) needs careful scheduling. Performance improvements can be + * an ongoing challenge. That's in "ehci-sched.c". + * + * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, + * or otherwise through transaction translators (TTs) in USB 2.0 hubs using + * (b) special fields in qh entries or (c) split iso entries. TTs will + * buffer low/full speed data so the host collects it at high speed. + */ + +/*-------------------------------------------------------------------------*/ + +/* fill a qtd, returning how much of the buffer we were able to queue up */ + +static int +qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, dma_addr_t buf, + size_t len, int token, int maxpacket) +{ + int i, count; + u64 addr = buf; + + /* one buffer entry per 4K ... first might be short or unaligned */ + qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr); + qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32)); + count = 0x1000 - (buf & 0x0fff); /* rest of that page */ + if (likely(len < count)) /* ... iff needed */ + count = len; + else { + buf += 0x1000; + buf &= ~0x0fff; + + /* per-qtd limit: from 16K to 20K (best alignment) */ + for (i = 1; count < len && i < 5; i++) { + addr = buf; + qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr); + qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210, + (u32)(addr >> 32)); + buf += 0x1000; + if ((count + 0x1000) < len) + count += 0x1000; + else + count = len; + } + + /* short packets may only terminate transfers */ + if (count != len) + count -= (count % maxpacket); + } + qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token); + qtd->length = count; + + return count; +} + +/*-------------------------------------------------------------------------*/ + +static inline void +qh_update(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, + struct fotg210_qtd *qtd) +{ + struct fotg210_qh_hw *hw = qh->hw; + + /* writes to an active overlay are unsafe */ + BUG_ON(qh->qh_state != QH_STATE_IDLE); + + hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma); + hw->hw_alt_next = FOTG210_LIST_END(fotg210); + + /* Except for control endpoints, we make hardware maintain data + * toggle (like OHCI) ... here (re)initialize the toggle in the QH, + * and set the pseudo-toggle in udev. Only usb_clear_halt() will + * ever clear it. + */ + if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) { + unsigned is_out, epnum; + + is_out = qh->is_out; + epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f; + if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { + hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE); + usb_settoggle(qh->dev, epnum, is_out, 1); + } + } + + hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING); +} + +/* if it weren't for a common silicon quirk (writing the dummy into the qh + * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault + * recovery (including urb dequeue) would need software changes to a QH... + */ +static void +qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + struct fotg210_qtd *qtd; + + if (list_empty(&qh->qtd_list)) + qtd = qh->dummy; + else { + qtd = list_entry(qh->qtd_list.next, + struct fotg210_qtd, qtd_list); + /* + * first qtd may already be partially processed. + * If we come here during unlink, the QH overlay region + * might have reference to the just unlinked qtd. The + * qtd is updated in qh_completions(). Update the QH + * overlay here. + */ + if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) { + qh->hw->hw_qtd_next = qtd->hw_next; + qtd = NULL; + } + } + + if (qtd) + qh_update(fotg210, qh, qtd); +} + +/*-------------------------------------------------------------------------*/ + +static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); + +static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_qh *qh = ep->hcpriv; + unsigned long flags; + + spin_lock_irqsave(&fotg210->lock, flags); + qh->clearing_tt = 0; + if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) + && fotg210->rh_state == FOTG210_RH_RUNNING) + qh_link_async(fotg210, qh); + spin_unlock_irqrestore(&fotg210->lock, flags); +} + +static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh, + struct urb *urb, u32 token) +{ + + /* If an async split transaction gets an error or is unlinked, + * the TT buffer may be left in an indeterminate state. We + * have to clear the TT buffer. + * + * Note: this routine is never called for Isochronous transfers. + */ + if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { +#ifdef DEBUG + struct usb_device *tt = urb->dev->tt->hub; + dev_dbg(&tt->dev, + "clear tt buffer port %d, a%d ep%d t%08x\n", + urb->dev->ttport, urb->dev->devnum, + usb_pipeendpoint(urb->pipe), token); +#endif /* DEBUG */ + if (urb->dev->tt->hub != + fotg210_to_hcd(fotg210)->self.root_hub) { + if (usb_hub_clear_tt_buffer(urb) == 0) + qh->clearing_tt = 1; + } + } +} + +static int qtd_copy_status( + struct fotg210_hcd *fotg210, + struct urb *urb, + size_t length, + u32 token +) +{ + int status = -EINPROGRESS; + + /* count IN/OUT bytes, not SETUP (even short packets) */ + if (likely(QTD_PID(token) != 2)) + urb->actual_length += length - QTD_LENGTH(token); + + /* don't modify error codes */ + if (unlikely(urb->unlinked)) + return status; + + /* force cleanup after short read; not always an error */ + if (unlikely(IS_SHORT_READ(token))) + status = -EREMOTEIO; + + /* serious "can't proceed" faults reported by the hardware */ + if (token & QTD_STS_HALT) { + if (token & QTD_STS_BABBLE) { + /* FIXME "must" disable babbling device's port too */ + status = -EOVERFLOW; + /* CERR nonzero + halt --> stall */ + } else if (QTD_CERR(token)) { + status = -EPIPE; + + /* In theory, more than one of the following bits can be set + * since they are sticky and the transaction is retried. + * Which to test first is rather arbitrary. + */ + } else if (token & QTD_STS_MMF) { + /* fs/ls interrupt xfer missed the complete-split */ + status = -EPROTO; + } else if (token & QTD_STS_DBE) { + status = (QTD_PID(token) == 1) /* IN ? */ + ? -ENOSR /* hc couldn't read data */ + : -ECOMM; /* hc couldn't write data */ + } else if (token & QTD_STS_XACT) { + /* timeout, bad CRC, wrong PID, etc */ + fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n", + urb->dev->devpath, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out"); + status = -EPROTO; + } else { /* unknown */ + status = -EPROTO; + } + + fotg210_vdbg(fotg210, + "dev%d ep%d%s qtd token %08x --> status %d\n", + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + token, status); + } + + return status; +} + +static void +fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, int status) +__releases(fotg210->lock) +__acquires(fotg210->lock) +{ + if (likely(urb->hcpriv != NULL)) { + struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; + + /* S-mask in a QH means it's an interrupt urb */ + if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) { + + /* ... update hc-wide periodic stats (for usbfs) */ + fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--; + } + } + + if (unlikely(urb->unlinked)) { + COUNT(fotg210->stats.unlink); + } else { + /* report non-error and short read status as zero */ + if (status == -EINPROGRESS || status == -EREMOTEIO) + status = 0; + COUNT(fotg210->stats.complete); + } + +#ifdef FOTG210_URB_TRACE + fotg210_dbg(fotg210, + "%s %s urb %p ep%d%s status %d len %d/%d\n", + __func__, urb->dev->devpath, urb, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + status, + urb->actual_length, urb->transfer_buffer_length); +#endif + + /* complete() can reenter this HCD */ + usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); + spin_unlock(&fotg210->lock); + usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status); + spin_lock(&fotg210->lock); +} + +static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); + +/* + * Process and free completed qtds for a qh, returning URBs to drivers. + * Chases up to qh->hw_current. Returns number of completions called, + * indicating how much "real" work we did. + */ +static unsigned +qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + struct fotg210_qtd *last, *end = qh->dummy; + struct list_head *entry, *tmp; + int last_status; + int stopped; + unsigned count = 0; + u8 state; + struct fotg210_qh_hw *hw = qh->hw; + + if (unlikely(list_empty(&qh->qtd_list))) + return count; + + /* completions (or tasks on other cpus) must never clobber HALT + * till we've gone through and cleaned everything up, even when + * they add urbs to this qh's queue or mark them for unlinking. + * + * NOTE: unlinking expects to be done in queue order. + * + * It's a bug for qh->qh_state to be anything other than + * QH_STATE_IDLE, unless our caller is scan_async() or + * scan_intr(). + */ + state = qh->qh_state; + qh->qh_state = QH_STATE_COMPLETING; + stopped = (state == QH_STATE_IDLE); + + rescan: + last = NULL; + last_status = -EINPROGRESS; + qh->needs_rescan = 0; + + /* remove de-activated QTDs from front of queue. + * after faults (including short reads), cleanup this urb + * then let the queue advance. + * if queue is stopped, handles unlinks. + */ + list_for_each_safe(entry, tmp, &qh->qtd_list) { + struct fotg210_qtd *qtd; + struct urb *urb; + u32 token = 0; + + qtd = list_entry(entry, struct fotg210_qtd, qtd_list); + urb = qtd->urb; + + /* clean up any state from previous QTD ...*/ + if (last) { + if (likely(last->urb != urb)) { + fotg210_urb_done(fotg210, last->urb, + last_status); + count++; + last_status = -EINPROGRESS; + } + fotg210_qtd_free(fotg210, last); + last = NULL; + } + + /* ignore urbs submitted during completions we reported */ + if (qtd == end) + break; + + /* hardware copies qtd out of qh overlay */ + rmb(); + token = hc32_to_cpu(fotg210, qtd->hw_token); + + /* always clean up qtds the hc de-activated */ + retry_xacterr: + if ((token & QTD_STS_ACTIVE) == 0) { + + /* Report Data Buffer Error: non-fatal but useful */ + if (token & QTD_STS_DBE) + fotg210_dbg(fotg210, + "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", + urb, + usb_endpoint_num(&urb->ep->desc), + usb_endpoint_dir_in(&urb->ep->desc) + ? "in" : "out", + urb->transfer_buffer_length, + qtd, + qh); + + /* on STALL, error, and short reads this urb must + * complete and all its qtds must be recycled. + */ + if ((token & QTD_STS_HALT) != 0) { + + /* retry transaction errors until we + * reach the software xacterr limit + */ + if ((token & QTD_STS_XACT) && + QTD_CERR(token) == 0 && + ++qh->xacterrs < QH_XACTERR_MAX && + !urb->unlinked) { + fotg210_dbg(fotg210, + "detected XactErr len %zu/%zu retry %d\n", + qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs); + + /* reset the token in the qtd and the + * qh overlay (which still contains + * the qtd) so that we pick up from + * where we left off + */ + token &= ~QTD_STS_HALT; + token |= QTD_STS_ACTIVE | + (FOTG210_TUNE_CERR << 10); + qtd->hw_token = cpu_to_hc32(fotg210, + token); + wmb(); + hw->hw_token = cpu_to_hc32(fotg210, + token); + goto retry_xacterr; + } + stopped = 1; + + /* magic dummy for some short reads; qh won't advance. + * that silicon quirk can kick in with this dummy too. + * + * other short reads won't stop the queue, including + * control transfers (status stage handles that) or + * most other single-qtd reads ... the queue stops if + * URB_SHORT_NOT_OK was set so the driver submitting + * the urbs could clean it up. + */ + } else if (IS_SHORT_READ(token) + && !(qtd->hw_alt_next + & FOTG210_LIST_END(fotg210))) { + stopped = 1; + } + + /* stop scanning when we reach qtds the hc is using */ + } else if (likely(!stopped + && fotg210->rh_state >= FOTG210_RH_RUNNING)) { + break; + + /* scan the whole queue for unlinks whenever it stops */ + } else { + stopped = 1; + + /* cancel everything if we halt, suspend, etc */ + if (fotg210->rh_state < FOTG210_RH_RUNNING) + last_status = -ESHUTDOWN; + + /* this qtd is active; skip it unless a previous qtd + * for its urb faulted, or its urb was canceled. + */ + else if (last_status == -EINPROGRESS && !urb->unlinked) + continue; + + /* qh unlinked; token in overlay may be most current */ + if (state == QH_STATE_IDLE + && cpu_to_hc32(fotg210, qtd->qtd_dma) + == hw->hw_current) { + token = hc32_to_cpu(fotg210, hw->hw_token); + + /* An unlink may leave an incomplete + * async transaction in the TT buffer. + * We have to clear it. + */ + fotg210_clear_tt_buffer(fotg210, qh, urb, + token); + } + } + + /* unless we already know the urb's status, collect qtd status + * and update count of bytes transferred. in common short read + * cases with only one data qtd (including control transfers), + * queue processing won't halt. but with two or more qtds (for + * example, with a 32 KB transfer), when the first qtd gets a + * short read the second must be removed by hand. + */ + if (last_status == -EINPROGRESS) { + last_status = qtd_copy_status(fotg210, urb, + qtd->length, token); + if (last_status == -EREMOTEIO + && (qtd->hw_alt_next + & FOTG210_LIST_END(fotg210))) + last_status = -EINPROGRESS; + + /* As part of low/full-speed endpoint-halt processing + * we must clear the TT buffer (11.17.5). + */ + if (unlikely(last_status != -EINPROGRESS && + last_status != -EREMOTEIO)) { + /* The TT's in some hubs malfunction when they + * receive this request following a STALL (they + * stop sending isochronous packets). Since a + * STALL can't leave the TT buffer in a busy + * state (if you believe Figures 11-48 - 11-51 + * in the USB 2.0 spec), we won't clear the TT + * buffer in this case. Strictly speaking this + * is a violation of the spec. + */ + if (last_status != -EPIPE) + fotg210_clear_tt_buffer(fotg210, qh, + urb, token); + } + } + + /* if we're removing something not at the queue head, + * patch the hardware queue pointer. + */ + if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { + last = list_entry(qtd->qtd_list.prev, + struct fotg210_qtd, qtd_list); + last->hw_next = qtd->hw_next; + } + + /* remove qtd; it's recycled after possible urb completion */ + list_del(&qtd->qtd_list); + last = qtd; + + /* reinit the xacterr counter for the next qtd */ + qh->xacterrs = 0; + } + + /* last urb's completion might still need calling */ + if (likely(last != NULL)) { + fotg210_urb_done(fotg210, last->urb, last_status); + count++; + fotg210_qtd_free(fotg210, last); + } + + /* Do we need to rescan for URBs dequeued during a giveback? */ + if (unlikely(qh->needs_rescan)) { + /* If the QH is already unlinked, do the rescan now. */ + if (state == QH_STATE_IDLE) + goto rescan; + + /* Otherwise we have to wait until the QH is fully unlinked. + * Our caller will start an unlink if qh->needs_rescan is + * set. But if an unlink has already started, nothing needs + * to be done. + */ + if (state != QH_STATE_LINKED) + qh->needs_rescan = 0; + } + + /* restore original state; caller must unlink or relink */ + qh->qh_state = state; + + /* be sure the hardware's done with the qh before refreshing + * it after fault cleanup, or recovering from silicon wrongly + * overlaying the dummy qtd (which reduces DMA chatter). + */ + if (stopped != 0 || hw->hw_qtd_next == FOTG210_LIST_END(fotg210)) { + switch (state) { + case QH_STATE_IDLE: + qh_refresh(fotg210, qh); + break; + case QH_STATE_LINKED: + /* We won't refresh a QH that's linked (after the HC + * stopped the queue). That avoids a race: + * - HC reads first part of QH; + * - CPU updates that first part and the token; + * - HC reads rest of that QH, including token + * Result: HC gets an inconsistent image, and then + * DMAs to/from the wrong memory (corrupting it). + * + * That should be rare for interrupt transfers, + * except maybe high bandwidth ... + */ + + /* Tell the caller to start an unlink */ + qh->needs_rescan = 1; + break; + /* otherwise, unlink already started */ + } + } + + return count; +} + +/*-------------------------------------------------------------------------*/ + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +/* ... and packet size, for any kind of endpoint descriptor */ +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + +/* + * reverse of qh_urb_transaction: free a list of TDs. + * used for cleanup after errors, before HC sees an URB's TDs. + */ +static void qtd_list_free( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *qtd_list +) { + struct list_head *entry, *temp; + + list_for_each_safe(entry, temp, qtd_list) { + struct fotg210_qtd *qtd; + + qtd = list_entry(entry, struct fotg210_qtd, qtd_list); + list_del(&qtd->qtd_list); + fotg210_qtd_free(fotg210, qtd); + } +} + +/* + * create a list of filled qtds for this URB; won't link into qh. + */ +static struct list_head * +qh_urb_transaction( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *head, + gfp_t flags +) { + struct fotg210_qtd *qtd, *qtd_prev; + dma_addr_t buf; + int len, this_sg_len, maxpacket; + int is_input; + u32 token; + int i; + struct scatterlist *sg; + + /* + * URBs map to sequences of QTDs: one logical transaction + */ + qtd = fotg210_qtd_alloc(fotg210, flags); + if (unlikely(!qtd)) + return NULL; + list_add_tail(&qtd->qtd_list, head); + qtd->urb = urb; + + token = QTD_STS_ACTIVE; + token |= (FOTG210_TUNE_CERR << 10); + /* for split transactions, SplitXState initialized to zero */ + + len = urb->transfer_buffer_length; + is_input = usb_pipein(urb->pipe); + if (usb_pipecontrol(urb->pipe)) { + /* SETUP pid */ + qtd_fill(fotg210, qtd, urb->setup_dma, + sizeof(struct usb_ctrlrequest), + token | (2 /* "setup" */ << 8), 8); + + /* ... and always at least one more pid */ + token ^= QTD_TOGGLE; + qtd_prev = qtd; + qtd = fotg210_qtd_alloc(fotg210, flags); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + + /* for zero length DATA stages, STATUS is always IN */ + if (len == 0) + token |= (1 /* "in" */ << 8); + } + + /* + * data transfer stage: buffer setup + */ + i = urb->num_mapped_sgs; + if (len > 0 && i > 0) { + sg = urb->sg; + buf = sg_dma_address(sg); + + /* urb->transfer_buffer_length may be smaller than the + * size of the scatterlist (or vice versa) + */ + this_sg_len = min_t(int, sg_dma_len(sg), len); + } else { + sg = NULL; + buf = urb->transfer_dma; + this_sg_len = len; + } + + if (is_input) + token |= (1 /* "in" */ << 8); + /* else it's already initted to "out" pid (0 << 8) */ + + maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); + + /* + * buffer gets wrapped in one or more qtds; + * last one may be "short" (including zero len) + * and may serve as a control status ack + */ + for (;;) { + int this_qtd_len; + + this_qtd_len = qtd_fill(fotg210, qtd, buf, this_sg_len, token, + maxpacket); + this_sg_len -= this_qtd_len; + len -= this_qtd_len; + buf += this_qtd_len; + + /* + * short reads advance to a "magic" dummy instead of the next + * qtd ... that forces the queue to stop, for manual cleanup. + * (this will usually be overridden later.) + */ + if (is_input) + qtd->hw_alt_next = fotg210->async->hw->hw_alt_next; + + /* qh makes control packets use qtd toggle; maybe switch it */ + if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) + token ^= QTD_TOGGLE; + + if (likely(this_sg_len <= 0)) { + if (--i <= 0 || len <= 0) + break; + sg = sg_next(sg); + buf = sg_dma_address(sg); + this_sg_len = min_t(int, sg_dma_len(sg), len); + } + + qtd_prev = qtd; + qtd = fotg210_qtd_alloc(fotg210, flags); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + } + + /* + * unless the caller requires manual cleanup after short reads, + * have the alt_next mechanism keep the queue running after the + * last data qtd (the only one, for control and most other cases). + */ + if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 + || usb_pipecontrol(urb->pipe))) + qtd->hw_alt_next = FOTG210_LIST_END(fotg210); + + /* + * control requests may need a terminating data "status" ack; + * other OUT ones may need a terminating short packet + * (zero length). + */ + if (likely(urb->transfer_buffer_length != 0)) { + int one_more = 0; + + if (usb_pipecontrol(urb->pipe)) { + one_more = 1; + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + } else if (usb_pipeout(urb->pipe) + && (urb->transfer_flags & URB_ZERO_PACKET) + && !(urb->transfer_buffer_length % maxpacket)) { + one_more = 1; + } + if (one_more) { + qtd_prev = qtd; + qtd = fotg210_qtd_alloc(fotg210, flags); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + + /* never any data in such packets */ + qtd_fill(fotg210, qtd, 0, 0, token, 0); + } + } + + /* by default, enable interrupt on urb completion */ + if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) + qtd->hw_token |= cpu_to_hc32(fotg210, QTD_IOC); + return head; + +cleanup: + qtd_list_free(fotg210, urb, head); + return NULL; +} + +/*-------------------------------------------------------------------------*/ +/* + * Would be best to create all qh's from config descriptors, + * when each interface/altsetting is established. Unlink + * any previous qh and cancel its urbs first; endpoints are + * implicitly reset then (data toggle too). + * That'd mean updating how usbcore talks to HCDs. (2.7?) +*/ + + +/* + * Each QH holds a qtd list; a QH is used for everything except iso. + * + * For interrupt urbs, the scheduler must set the microframe scheduling + * mask(s) each time the QH gets scheduled. For highspeed, that's + * just one microframe in the s-mask. For split interrupt transactions + * there are additional complications: c-mask, maybe FSTNs. + */ +static struct fotg210_qh * +qh_make( + struct fotg210_hcd *fotg210, + struct urb *urb, + gfp_t flags +) { + struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); + u32 info1 = 0, info2 = 0; + int is_input, type; + int maxp = 0; + struct usb_tt *tt = urb->dev->tt; + struct fotg210_qh_hw *hw; + + if (!qh) + return qh; + + /* + * init endpoint/device data for this QH + */ + info1 |= usb_pipeendpoint(urb->pipe) << 8; + info1 |= usb_pipedevice(urb->pipe) << 0; + + is_input = usb_pipein(urb->pipe); + type = usb_pipetype(urb->pipe); + maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input); + + /* 1024 byte maxpacket is a hardware ceiling. High bandwidth + * acts like up to 3KB, but is built from smaller packets. + */ + if (max_packet(maxp) > 1024) { + fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", + max_packet(maxp)); + goto done; + } + + /* Compute interrupt scheduling parameters just once, and save. + * - allowing for high bandwidth, how many nsec/uframe are used? + * - split transactions need a second CSPLIT uframe; same question + * - splits also need a schedule gap (for full/low speed I/O) + * - qh has a polling interval + * + * For control/bulk requests, the HC or TT handles these. + */ + if (type == PIPE_INTERRUPT) { + qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, + is_input, 0, + hb_mult(maxp) * max_packet(maxp))); + qh->start = NO_FRAME; + + if (urb->dev->speed == USB_SPEED_HIGH) { + qh->c_usecs = 0; + qh->gap_uf = 0; + + qh->period = urb->interval >> 3; + if (qh->period == 0 && urb->interval != 1) { + /* NOTE interval 2 or 4 uframes could work. + * But interval 1 scheduling is simpler, and + * includes high bandwidth. + */ + urb->interval = 1; + } else if (qh->period > fotg210->periodic_size) { + qh->period = fotg210->periodic_size; + urb->interval = qh->period << 3; + } + } else { + int think_time; + + /* gap is f(FS/LS transfer times) */ + qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, + is_input, 0, maxp) / (125 * 1000); + + /* FIXME this just approximates SPLIT/CSPLIT times */ + if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ + qh->c_usecs = qh->usecs + HS_USECS(0); + qh->usecs = HS_USECS(1); + } else { /* SPLIT+DATA, gap, CSPLIT */ + qh->usecs += HS_USECS(1); + qh->c_usecs = HS_USECS(0); + } + + think_time = tt ? tt->think_time : 0; + qh->tt_usecs = NS_TO_US(think_time + + usb_calc_bus_time(urb->dev->speed, + is_input, 0, max_packet(maxp))); + qh->period = urb->interval; + if (qh->period > fotg210->periodic_size) { + qh->period = fotg210->periodic_size; + urb->interval = qh->period; + } + } + } + + /* support for tt scheduling, and access to toggles */ + qh->dev = urb->dev; + + /* using TT? */ + switch (urb->dev->speed) { + case USB_SPEED_LOW: + info1 |= QH_LOW_SPEED; + /* FALL THROUGH */ + + case USB_SPEED_FULL: + /* EPS 0 means "full" */ + if (type != PIPE_INTERRUPT) + info1 |= (FOTG210_TUNE_RL_TT << 28); + if (type == PIPE_CONTROL) { + info1 |= QH_CONTROL_EP; /* for TT */ + info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ + } + info1 |= maxp << 16; + + info2 |= (FOTG210_TUNE_MULT_TT << 30); + + /* Some Freescale processors have an erratum in which the + * port number in the queue head was 0..N-1 instead of 1..N. + */ + if (fotg210_has_fsl_portno_bug(fotg210)) + info2 |= (urb->dev->ttport-1) << 23; + else + info2 |= urb->dev->ttport << 23; + + /* set the address of the TT; for TDI's integrated + * root hub tt, leave it zeroed. + */ + if (tt && tt->hub != fotg210_to_hcd(fotg210)->self.root_hub) + info2 |= tt->hub->devnum << 16; + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ + + break; + + case USB_SPEED_HIGH: /* no TT involved */ + info1 |= QH_HIGH_SPEED; + if (type == PIPE_CONTROL) { + info1 |= (FOTG210_TUNE_RL_HS << 28); + info1 |= 64 << 16; /* usb2 fixed maxpacket */ + info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ + info2 |= (FOTG210_TUNE_MULT_HS << 30); + } else if (type == PIPE_BULK) { + info1 |= (FOTG210_TUNE_RL_HS << 28); + /* The USB spec says that high speed bulk endpoints + * always use 512 byte maxpacket. But some device + * vendors decided to ignore that, and MSFT is happy + * to help them do so. So now people expect to use + * such nonconformant devices with Linux too; sigh. + */ + info1 |= max_packet(maxp) << 16; + info2 |= (FOTG210_TUNE_MULT_HS << 30); + } else { /* PIPE_INTERRUPT */ + info1 |= max_packet(maxp) << 16; + info2 |= hb_mult(maxp) << 30; + } + break; + default: + fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev, + urb->dev->speed); +done: + qh_destroy(fotg210, qh); + return NULL; + } + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ + + /* init as live, toggle clear, advance to dummy */ + qh->qh_state = QH_STATE_IDLE; + hw = qh->hw; + hw->hw_info1 = cpu_to_hc32(fotg210, info1); + hw->hw_info2 = cpu_to_hc32(fotg210, info2); + qh->is_out = !is_input; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); + qh_refresh(fotg210, qh); + return qh; +} + +/*-------------------------------------------------------------------------*/ + +static void enable_async(struct fotg210_hcd *fotg210) +{ + if (fotg210->async_count++) + return; + + /* Stop waiting to turn off the async schedule */ + fotg210->enabled_hrtimer_events &= ~BIT(FOTG210_HRTIMER_DISABLE_ASYNC); + + /* Don't start the schedule until ASS is 0 */ + fotg210_poll_ASS(fotg210); + turn_on_io_watchdog(fotg210); +} + +static void disable_async(struct fotg210_hcd *fotg210) +{ + if (--fotg210->async_count) + return; + + /* The async schedule and async_unlink list are supposed to be empty */ + WARN_ON(fotg210->async->qh_next.qh || fotg210->async_unlink); + + /* Don't turn off the schedule until ASS is 1 */ + fotg210_poll_ASS(fotg210); +} + +/* move qh (and its qtds) onto async queue; maybe enable queue. */ + +static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); + struct fotg210_qh *head; + + /* Don't link a QH if there's a Clear-TT-Buffer pending */ + if (unlikely(qh->clearing_tt)) + return; + + WARN_ON(qh->qh_state != QH_STATE_IDLE); + + /* clear halt and/or toggle; and maybe recover from silicon quirk */ + qh_refresh(fotg210, qh); + + /* splice right after start */ + head = fotg210->async; + qh->qh_next = head->qh_next; + qh->hw->hw_next = head->hw->hw_next; + wmb(); + + head->qh_next.qh = qh; + head->hw->hw_next = dma; + + qh->xacterrs = 0; + qh->qh_state = QH_STATE_LINKED; + /* qtd completions reported later by interrupt */ + + enable_async(fotg210); +} + +/*-------------------------------------------------------------------------*/ + +/* + * For control/bulk/interrupt, return QH with these TDs appended. + * Allocates and initializes the QH if necessary. + * Returns null if it can't allocate a QH it needs to. + * If the QH has TDs (urbs) already, that's great. + */ +static struct fotg210_qh *qh_append_tds( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *qtd_list, + int epnum, + void **ptr +) +{ + struct fotg210_qh *qh = NULL; + __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); + + qh = (struct fotg210_qh *) *ptr; + if (unlikely(qh == NULL)) { + /* can't sleep here, we have fotg210->lock... */ + qh = qh_make(fotg210, urb, GFP_ATOMIC); + *ptr = qh; + } + if (likely(qh != NULL)) { + struct fotg210_qtd *qtd; + + if (unlikely(list_empty(qtd_list))) + qtd = NULL; + else + qtd = list_entry(qtd_list->next, struct fotg210_qtd, + qtd_list); + + /* control qh may need patching ... */ + if (unlikely(epnum == 0)) { + /* usb_reset_device() briefly reverts to address 0 */ + if (usb_pipedevice(urb->pipe) == 0) + qh->hw->hw_info1 &= ~qh_addr_mask; + } + + /* just one way to queue requests: swap with the dummy qtd. + * only hc or qh_refresh() ever modify the overlay. + */ + if (likely(qtd != NULL)) { + struct fotg210_qtd *dummy; + dma_addr_t dma; + __hc32 token; + + /* to avoid racing the HC, use the dummy td instead of + * the first td of our list (becomes new dummy). both + * tds stay deactivated until we're done, when the + * HC is allowed to fetch the old dummy (4.10.2). + */ + token = qtd->hw_token; + qtd->hw_token = HALT_BIT(fotg210); + + dummy = qh->dummy; + + dma = dummy->qtd_dma; + *dummy = *qtd; + dummy->qtd_dma = dma; + + list_del(&qtd->qtd_list); + list_add(&dummy->qtd_list, qtd_list); + list_splice_tail(qtd_list, &qh->qtd_list); + + fotg210_qtd_init(fotg210, qtd, qtd->qtd_dma); + qh->dummy = qtd; + + /* hc must see the new dummy at list end */ + dma = qtd->qtd_dma; + qtd = list_entry(qh->qtd_list.prev, + struct fotg210_qtd, qtd_list); + qtd->hw_next = QTD_NEXT(fotg210, dma); + + /* let the hc process these next qtds */ + wmb(); + dummy->hw_token = token; + + urb->hcpriv = qh; + } + } + return qh; +} + +/*-------------------------------------------------------------------------*/ + +static int +submit_async( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *qtd_list, + gfp_t mem_flags +) { + int epnum; + unsigned long flags; + struct fotg210_qh *qh = NULL; + int rc; + + epnum = urb->ep->desc.bEndpointAddress; + +#ifdef FOTG210_URB_TRACE + { + struct fotg210_qtd *qtd; + qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list); + fotg210_dbg(fotg210, + "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", + __func__, urb->dev->devpath, urb, + epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", + urb->transfer_buffer_length, + qtd, urb->ep->hcpriv); + } +#endif + + spin_lock_irqsave(&fotg210->lock, flags); + if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { + rc = -ESHUTDOWN; + goto done; + } + rc = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); + if (unlikely(rc)) + goto done; + + qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); + if (unlikely(qh == NULL)) { + usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); + rc = -ENOMEM; + goto done; + } + + /* Control/bulk operations through TTs don't need scheduling, + * the HC and TT handle it when the TT has a buffer ready. + */ + if (likely(qh->qh_state == QH_STATE_IDLE)) + qh_link_async(fotg210, qh); + done: + spin_unlock_irqrestore(&fotg210->lock, flags); + if (unlikely(qh == NULL)) + qtd_list_free(fotg210, urb, qtd_list); + return rc; +} + +/*-------------------------------------------------------------------------*/ + +static void single_unlink_async(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh) +{ + struct fotg210_qh *prev; + + /* Add to the end of the list of QHs waiting for the next IAAD */ + qh->qh_state = QH_STATE_UNLINK; + if (fotg210->async_unlink) + fotg210->async_unlink_last->unlink_next = qh; + else + fotg210->async_unlink = qh; + fotg210->async_unlink_last = qh; + + /* Unlink it from the schedule */ + prev = fotg210->async; + while (prev->qh_next.qh != qh) + prev = prev->qh_next.qh; + + prev->hw->hw_next = qh->hw->hw_next; + prev->qh_next = qh->qh_next; + if (fotg210->qh_scan_next == qh) + fotg210->qh_scan_next = qh->qh_next.qh; +} + +static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) +{ + /* + * Do nothing if an IAA cycle is already running or + * if one will be started shortly. + */ + if (fotg210->async_iaa || fotg210->async_unlinking) + return; + + /* Do all the waiting QHs at once */ + fotg210->async_iaa = fotg210->async_unlink; + fotg210->async_unlink = NULL; + + /* If the controller isn't running, we don't have to wait for it */ + if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) { + if (!nested) /* Avoid recursion */ + end_unlink_async(fotg210); + + /* Otherwise start a new IAA cycle */ + } else if (likely(fotg210->rh_state == FOTG210_RH_RUNNING)) { + /* Make sure the unlinks are all visible to the hardware */ + wmb(); + + fotg210_writel(fotg210, fotg210->command | CMD_IAAD, + &fotg210->regs->command); + fotg210_readl(fotg210, &fotg210->regs->command); + fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG, + true); + } +} + +/* the async qh for the qtds being unlinked are now gone from the HC */ + +static void end_unlink_async(struct fotg210_hcd *fotg210) +{ + struct fotg210_qh *qh; + + /* Process the idle QHs */ + restart: + fotg210->async_unlinking = true; + while (fotg210->async_iaa) { + qh = fotg210->async_iaa; + fotg210->async_iaa = qh->unlink_next; + qh->unlink_next = NULL; + + qh->qh_state = QH_STATE_IDLE; + qh->qh_next.qh = NULL; + + qh_completions(fotg210, qh); + if (!list_empty(&qh->qtd_list) && + fotg210->rh_state == FOTG210_RH_RUNNING) + qh_link_async(fotg210, qh); + disable_async(fotg210); + } + fotg210->async_unlinking = false; + + /* Start a new IAA cycle if any QHs are waiting for it */ + if (fotg210->async_unlink) { + start_iaa_cycle(fotg210, true); + if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) + goto restart; + } +} + +static void unlink_empty_async(struct fotg210_hcd *fotg210) +{ + struct fotg210_qh *qh, *next; + bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); + bool check_unlinks_later = false; + + /* Unlink all the async QHs that have been empty for a timer cycle */ + next = fotg210->async->qh_next.qh; + while (next) { + qh = next; + next = qh->qh_next.qh; + + if (list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED) { + if (!stopped && qh->unlink_cycle == + fotg210->async_unlink_cycle) + check_unlinks_later = true; + else + single_unlink_async(fotg210, qh); + } + } + + /* Start a new IAA cycle if any QHs are waiting for it */ + if (fotg210->async_unlink) + start_iaa_cycle(fotg210, false); + + /* QHs that haven't been empty for long enough will be handled later */ + if (check_unlinks_later) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS, + true); + ++fotg210->async_unlink_cycle; + } +} + +/* makes sure the async qh will become idle */ +/* caller must own fotg210->lock */ + +static void start_unlink_async(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh) +{ + /* + * If the QH isn't linked then there's nothing we can do + * unless we were called during a giveback, in which case + * qh_completions() has to deal with it. + */ + if (qh->qh_state != QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_COMPLETING) + qh->needs_rescan = 1; + return; + } + + single_unlink_async(fotg210, qh); + start_iaa_cycle(fotg210, false); +} + +/*-------------------------------------------------------------------------*/ + +static void scan_async(struct fotg210_hcd *fotg210) +{ + struct fotg210_qh *qh; + bool check_unlinks_later = false; + + fotg210->qh_scan_next = fotg210->async->qh_next.qh; + while (fotg210->qh_scan_next) { + qh = fotg210->qh_scan_next; + fotg210->qh_scan_next = qh->qh_next.qh; + rescan: + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list)) { + int temp; + + /* + * Unlinks could happen here; completion reporting + * drops the lock. That's why fotg210->qh_scan_next + * always holds the next qh to scan; if the next qh + * gets unlinked then fotg210->qh_scan_next is adjusted + * in single_unlink_async(). + */ + temp = qh_completions(fotg210, qh); + if (qh->needs_rescan) { + start_unlink_async(fotg210, qh); + } else if (list_empty(&qh->qtd_list) + && qh->qh_state == QH_STATE_LINKED) { + qh->unlink_cycle = fotg210->async_unlink_cycle; + check_unlinks_later = true; + } else if (temp != 0) + goto rescan; + } + } + + /* + * Unlink empty entries, reducing DMA usage as well + * as HCD schedule-scanning costs. Delay for any qh + * we just scanned, there's a not-unusual case that it + * doesn't stay idle for long. + */ + if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING && + !(fotg210->enabled_hrtimer_events & + BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { + fotg210_enable_event(fotg210, + FOTG210_HRTIMER_ASYNC_UNLINKS, true); + ++fotg210->async_unlink_cycle; + } +} +/*-------------------------------------------------------------------------*/ +/* + * EHCI scheduled transaction support: interrupt, iso, split iso + * These are called "periodic" transactions in the EHCI spec. + * + * Note that for interrupt transfers, the QH/QTD manipulation is shared + * with the "asynchronous" transaction support (control/bulk transfers). + * The only real difference is in how interrupt transfers are scheduled. + * + * For ISO, we make an "iso_stream" head to serve the same role as a QH. + * It keeps track of every ITD (or SITD) that's linked, and holds enough + * pre-calculated schedule data to make appending to the queue be quick. + */ + +static int fotg210_get_frame(struct usb_hcd *hcd); + +/*-------------------------------------------------------------------------*/ + +/* + * periodic_next_shadow - return "next" pointer on shadow list + * @periodic: host pointer to qh/itd + * @tag: hardware tag for type of this record + */ +static union fotg210_shadow * +periodic_next_shadow(struct fotg210_hcd *fotg210, + union fotg210_shadow *periodic, __hc32 tag) +{ + switch (hc32_to_cpu(fotg210, tag)) { + case Q_TYPE_QH: + return &periodic->qh->qh_next; + case Q_TYPE_FSTN: + return &periodic->fstn->fstn_next; + default: + return &periodic->itd->itd_next; + } +} + +static __hc32 * +shadow_next_periodic(struct fotg210_hcd *fotg210, + union fotg210_shadow *periodic, __hc32 tag) +{ + switch (hc32_to_cpu(fotg210, tag)) { + /* our fotg210_shadow.qh is actually software part */ + case Q_TYPE_QH: + return &periodic->qh->hw->hw_next; + /* others are hw parts */ + default: + return periodic->hw_next; + } +} + +/* caller must hold fotg210->lock */ +static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, + void *ptr) +{ + union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; + __hc32 *hw_p = &fotg210->periodic[frame]; + union fotg210_shadow here = *prev_p; + + /* find predecessor of "ptr"; hw and shadow lists are in sync */ + while (here.ptr && here.ptr != ptr) { + prev_p = periodic_next_shadow(fotg210, prev_p, + Q_NEXT_TYPE(fotg210, *hw_p)); + hw_p = shadow_next_periodic(fotg210, &here, + Q_NEXT_TYPE(fotg210, *hw_p)); + here = *prev_p; + } + /* an interrupt entry (at list end) could have been shared */ + if (!here.ptr) + return; + + /* update shadow and hardware lists ... the old "next" pointers + * from ptr may still be in use, the caller updates them. + */ + *prev_p = *periodic_next_shadow(fotg210, &here, + Q_NEXT_TYPE(fotg210, *hw_p)); + + *hw_p = *shadow_next_periodic(fotg210, &here, + Q_NEXT_TYPE(fotg210, *hw_p)); +} + +/* how many of the uframe's 125 usecs are allocated? */ +static unsigned short +periodic_usecs(struct fotg210_hcd *fotg210, unsigned frame, unsigned uframe) +{ + __hc32 *hw_p = &fotg210->periodic[frame]; + union fotg210_shadow *q = &fotg210->pshadow[frame]; + unsigned usecs = 0; + struct fotg210_qh_hw *hw; + + while (q->ptr) { + switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) { + case Q_TYPE_QH: + hw = q->qh->hw; + /* is it in the S-mask? */ + if (hw->hw_info2 & cpu_to_hc32(fotg210, 1 << uframe)) + usecs += q->qh->usecs; + /* ... or C-mask? */ + if (hw->hw_info2 & cpu_to_hc32(fotg210, + 1 << (8 + uframe))) + usecs += q->qh->c_usecs; + hw_p = &hw->hw_next; + q = &q->qh->qh_next; + break; + /* case Q_TYPE_FSTN: */ + default: + /* for "save place" FSTNs, count the relevant INTR + * bandwidth from the previous frame + */ + if (q->fstn->hw_prev != FOTG210_LIST_END(fotg210)) + fotg210_dbg(fotg210, "ignoring FSTN cost ...\n"); + + hw_p = &q->fstn->hw_next; + q = &q->fstn->fstn_next; + break; + case Q_TYPE_ITD: + if (q->itd->hw_transaction[uframe]) + usecs += q->itd->stream->usecs; + hw_p = &q->itd->hw_next; + q = &q->itd->itd_next; + break; + } + } +#ifdef DEBUG + if (usecs > fotg210->uframe_periodic_max) + fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", + frame * 8 + uframe, usecs); +#endif + return usecs; +} + +/*-------------------------------------------------------------------------*/ + +static int same_tt(struct usb_device *dev1, struct usb_device *dev2) +{ + if (!dev1->tt || !dev2->tt) + return 0; + if (dev1->tt != dev2->tt) + return 0; + if (dev1->tt->multi) + return dev1->ttport == dev2->ttport; + else + return 1; +} + +/* return true iff the device's transaction translator is available + * for a periodic transfer starting at the specified frame, using + * all the uframes in the mask. + */ +static int tt_no_collision( + struct fotg210_hcd *fotg210, + unsigned period, + struct usb_device *dev, + unsigned frame, + u32 uf_mask +) +{ + if (period == 0) /* error */ + return 0; + + /* note bandwidth wastage: split never follows csplit + * (different dev or endpoint) until the next uframe. + * calling convention doesn't make that distinction. + */ + for (; frame < fotg210->periodic_size; frame += period) { + union fotg210_shadow here; + __hc32 type; + struct fotg210_qh_hw *hw; + + here = fotg210->pshadow[frame]; + type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]); + while (here.ptr) { + switch (hc32_to_cpu(fotg210, type)) { + case Q_TYPE_ITD: + type = Q_NEXT_TYPE(fotg210, here.itd->hw_next); + here = here.itd->itd_next; + continue; + case Q_TYPE_QH: + hw = here.qh->hw; + if (same_tt(dev, here.qh->dev)) { + u32 mask; + + mask = hc32_to_cpu(fotg210, + hw->hw_info2); + /* "knows" no gap is needed */ + mask |= mask >> 8; + if (mask & uf_mask) + break; + } + type = Q_NEXT_TYPE(fotg210, hw->hw_next); + here = here.qh->qh_next; + continue; + /* case Q_TYPE_FSTN: */ + default: + fotg210_dbg(fotg210, + "periodic frame %d bogus type %d\n", + frame, type); + } + + /* collision or error */ + return 0; + } + } + + /* no collision */ + return 1; +} + +/*-------------------------------------------------------------------------*/ + +static void enable_periodic(struct fotg210_hcd *fotg210) +{ + if (fotg210->periodic_count++) + return; + + /* Stop waiting to turn off the periodic schedule */ + fotg210->enabled_hrtimer_events &= + ~BIT(FOTG210_HRTIMER_DISABLE_PERIODIC); + + /* Don't start the schedule until PSS is 0 */ + fotg210_poll_PSS(fotg210); + turn_on_io_watchdog(fotg210); +} + +static void disable_periodic(struct fotg210_hcd *fotg210) +{ + if (--fotg210->periodic_count) + return; + + /* Don't turn off the schedule until PSS is 1 */ + fotg210_poll_PSS(fotg210); +} + +/*-------------------------------------------------------------------------*/ + +/* periodic schedule slots have iso tds (normal or split) first, then a + * sparse tree for active interrupt transfers. + * + * this just links in a qh; caller guarantees uframe masks are set right. + * no FSTN support (yet; fotg210 0.96+) + */ +static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + unsigned i; + unsigned period = qh->period; + + dev_dbg(&qh->dev->dev, + "link qh%d-%04x/%p start %d [%d/%d us]\n", + period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) + & (QH_CMASK | QH_SMASK), + qh, qh->start, qh->usecs, qh->c_usecs); + + /* high bandwidth, or otherwise every microframe */ + if (period == 0) + period = 1; + + for (i = qh->start; i < fotg210->periodic_size; i += period) { + union fotg210_shadow *prev = &fotg210->pshadow[i]; + __hc32 *hw_p = &fotg210->periodic[i]; + union fotg210_shadow here = *prev; + __hc32 type = 0; + + /* skip the iso nodes at list head */ + while (here.ptr) { + type = Q_NEXT_TYPE(fotg210, *hw_p); + if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) + break; + prev = periodic_next_shadow(fotg210, prev, type); + hw_p = shadow_next_periodic(fotg210, &here, type); + here = *prev; + } + + /* sorting each branch by period (slow-->fast) + * enables sharing interior tree nodes + */ + while (here.ptr && qh != here.qh) { + if (qh->period > here.qh->period) + break; + prev = &here.qh->qh_next; + hw_p = &here.qh->hw->hw_next; + here = *prev; + } + /* link in this qh, unless some earlier pass did that */ + if (qh != here.qh) { + qh->qh_next = here; + if (here.qh) + qh->hw->hw_next = *hw_p; + wmb(); + prev->qh = qh; + *hw_p = QH_NEXT(fotg210, qh->qh_dma); + } + } + qh->qh_state = QH_STATE_LINKED; + qh->xacterrs = 0; + + /* update per-qh bandwidth for usbfs */ + fotg210_to_hcd(fotg210)->self.bandwidth_allocated += qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + list_add(&qh->intr_node, &fotg210->intr_qh_list); + + /* maybe enable periodic schedule processing */ + ++fotg210->intr_count; + enable_periodic(fotg210); +} + +static void qh_unlink_periodic(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh) +{ + unsigned i; + unsigned period; + + /* + * If qh is for a low/full-speed device, simply unlinking it + * could interfere with an ongoing split transaction. To unlink + * it safely would require setting the QH_INACTIVATE bit and + * waiting at least one frame, as described in EHCI 4.12.2.5. + * + * We won't bother with any of this. Instead, we assume that the + * only reason for unlinking an interrupt QH while the current URB + * is still active is to dequeue all the URBs (flush the whole + * endpoint queue). + * + * If rebalancing the periodic schedule is ever implemented, this + * approach will no longer be valid. + */ + + /* high bandwidth, or otherwise part of every microframe */ + period = qh->period; + if (!period) + period = 1; + + for (i = qh->start; i < fotg210->periodic_size; i += period) + periodic_unlink(fotg210, i, qh); + + /* update per-qh bandwidth for usbfs */ + fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + dev_dbg(&qh->dev->dev, + "unlink qh%d-%04x/%p start %d [%d/%d us]\n", + qh->period, + hc32_to_cpup(fotg210, &qh->hw->hw_info2) & + (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); + + /* qh->qh_next still "live" to HC */ + qh->qh_state = QH_STATE_UNLINK; + qh->qh_next.ptr = NULL; + + if (fotg210->qh_scan_next == qh) + fotg210->qh_scan_next = list_entry(qh->intr_node.next, + struct fotg210_qh, intr_node); + list_del(&qh->intr_node); +} + +static void start_unlink_intr(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh) +{ + /* If the QH isn't linked then there's nothing we can do + * unless we were called during a giveback, in which case + * qh_completions() has to deal with it. + */ + if (qh->qh_state != QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_COMPLETING) + qh->needs_rescan = 1; + return; + } + + qh_unlink_periodic(fotg210, qh); + + /* Make sure the unlinks are visible before starting the timer */ + wmb(); + + /* + * The EHCI spec doesn't say how long it takes the controller to + * stop accessing an unlinked interrupt QH. The timer delay is + * 9 uframes; presumably that will be long enough. + */ + qh->unlink_cycle = fotg210->intr_unlink_cycle; + + /* New entries go at the end of the intr_unlink list */ + if (fotg210->intr_unlink) + fotg210->intr_unlink_last->unlink_next = qh; + else + fotg210->intr_unlink = qh; + fotg210->intr_unlink_last = qh; + + if (fotg210->intr_unlinking) + ; /* Avoid recursive calls */ + else if (fotg210->rh_state < FOTG210_RH_RUNNING) + fotg210_handle_intr_unlinks(fotg210); + else if (fotg210->intr_unlink == qh) { + fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, + true); + ++fotg210->intr_unlink_cycle; + } +} + +static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + struct fotg210_qh_hw *hw = qh->hw; + int rc; + + qh->qh_state = QH_STATE_IDLE; + hw->hw_next = FOTG210_LIST_END(fotg210); + + qh_completions(fotg210, qh); + + /* reschedule QH iff another request is queued */ + if (!list_empty(&qh->qtd_list) && + fotg210->rh_state == FOTG210_RH_RUNNING) { + rc = qh_schedule(fotg210, qh); + + /* An error here likely indicates handshake failure + * or no space left in the schedule. Neither fault + * should happen often ... + * + * FIXME kill the now-dysfunctional queued urbs + */ + if (rc != 0) + fotg210_err(fotg210, "can't reschedule qh %p, err %d\n", + qh, rc); + } + + /* maybe turn off periodic schedule */ + --fotg210->intr_count; + disable_periodic(fotg210); +} + +/*-------------------------------------------------------------------------*/ + +static int check_period( + struct fotg210_hcd *fotg210, + unsigned frame, + unsigned uframe, + unsigned period, + unsigned usecs +) { + int claimed; + + /* complete split running into next frame? + * given FSTN support, we could sometimes check... + */ + if (uframe >= 8) + return 0; + + /* convert "usecs we need" to "max already claimed" */ + usecs = fotg210->uframe_periodic_max - usecs; + + /* we "know" 2 and 4 uframe intervals were rejected; so + * for period 0, check _every_ microframe in the schedule. + */ + if (unlikely(period == 0)) { + do { + for (uframe = 0; uframe < 7; uframe++) { + claimed = periodic_usecs(fotg210, frame, + uframe); + if (claimed > usecs) + return 0; + } + } while ((frame += 1) < fotg210->periodic_size); + + /* just check the specified uframe, at that period */ + } else { + do { + claimed = periodic_usecs(fotg210, frame, uframe); + if (claimed > usecs) + return 0; + } while ((frame += period) < fotg210->periodic_size); + } + + /* success! */ + return 1; +} + +static int check_intr_schedule( + struct fotg210_hcd *fotg210, + unsigned frame, + unsigned uframe, + const struct fotg210_qh *qh, + __hc32 *c_maskp +) +{ + int retval = -ENOSPC; + u8 mask = 0; + + if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ + goto done; + + if (!check_period(fotg210, frame, uframe, qh->period, qh->usecs)) + goto done; + if (!qh->c_usecs) { + retval = 0; + *c_maskp = 0; + goto done; + } + + /* Make sure this tt's buffer is also available for CSPLITs. + * We pessimize a bit; probably the typical full speed case + * doesn't need the second CSPLIT. + * + * NOTE: both SPLIT and CSPLIT could be checked in just + * one smart pass... + */ + mask = 0x03 << (uframe + qh->gap_uf); + *c_maskp = cpu_to_hc32(fotg210, mask << 8); + + mask |= 1 << uframe; + if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) { + if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1, + qh->period, qh->c_usecs)) + goto done; + if (!check_period(fotg210, frame, uframe + qh->gap_uf, + qh->period, qh->c_usecs)) + goto done; + retval = 0; + } +done: + return retval; +} + +/* "first fit" scheduling policy used the first time through, + * or when the previous schedule slot can't be re-used. + */ +static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +{ + int status; + unsigned uframe; + __hc32 c_mask; + unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + struct fotg210_qh_hw *hw = qh->hw; + + qh_refresh(fotg210, qh); + hw->hw_next = FOTG210_LIST_END(fotg210); + frame = qh->start; + + /* reuse the previous schedule slots, if we can */ + if (frame < qh->period) { + uframe = ffs(hc32_to_cpup(fotg210, &hw->hw_info2) & QH_SMASK); + status = check_intr_schedule(fotg210, frame, --uframe, + qh, &c_mask); + } else { + uframe = 0; + c_mask = 0; + status = -ENOSPC; + } + + /* else scan the schedule to find a group of slots such that all + * uframes have enough periodic bandwidth available. + */ + if (status) { + /* "normal" case, uframing flexible except with splits */ + if (qh->period) { + int i; + + for (i = qh->period; status && i > 0; --i) { + frame = ++fotg210->random_frame % qh->period; + for (uframe = 0; uframe < 8; uframe++) { + status = check_intr_schedule(fotg210, + frame, uframe, qh, + &c_mask); + if (status == 0) + break; + } + } + + /* qh->period == 0 means every uframe */ + } else { + frame = 0; + status = check_intr_schedule(fotg210, 0, 0, qh, + &c_mask); + } + if (status) + goto done; + qh->start = frame; + + /* reset S-frame and (maybe) C-frame masks */ + hw->hw_info2 &= cpu_to_hc32(fotg210, ~(QH_CMASK | QH_SMASK)); + hw->hw_info2 |= qh->period + ? cpu_to_hc32(fotg210, 1 << uframe) + : cpu_to_hc32(fotg210, QH_SMASK); + hw->hw_info2 |= c_mask; + } else + fotg210_dbg(fotg210, "reused qh %p schedule\n", qh); + + /* stuff into the periodic schedule */ + qh_link_periodic(fotg210, qh); +done: + return status; +} + +static int intr_submit( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct list_head *qtd_list, + gfp_t mem_flags +) { + unsigned epnum; + unsigned long flags; + struct fotg210_qh *qh; + int status; + struct list_head empty; + + /* get endpoint and transfer/schedule data */ + epnum = urb->ep->desc.bEndpointAddress; + + spin_lock_irqsave(&fotg210->lock, flags); + + if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { + status = -ESHUTDOWN; + goto done_not_linked; + } + status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); + if (unlikely(status)) + goto done_not_linked; + + /* get qh and force any scheduling errors */ + INIT_LIST_HEAD(&empty); + qh = qh_append_tds(fotg210, urb, &empty, epnum, &urb->ep->hcpriv); + if (qh == NULL) { + status = -ENOMEM; + goto done; + } + if (qh->qh_state == QH_STATE_IDLE) { + status = qh_schedule(fotg210, qh); + if (status) + goto done; + } + + /* then queue the urb's tds to the qh */ + qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); + BUG_ON(qh == NULL); + + /* ... update usbfs periodic stats */ + fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs++; + +done: + if (unlikely(status)) + usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); +done_not_linked: + spin_unlock_irqrestore(&fotg210->lock, flags); + if (status) + qtd_list_free(fotg210, urb, qtd_list); + + return status; +} + +static void scan_intr(struct fotg210_hcd *fotg210) +{ + struct fotg210_qh *qh; + + list_for_each_entry_safe(qh, fotg210->qh_scan_next, + &fotg210->intr_qh_list, intr_node) { + rescan: + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list)) { + int temp; + + /* + * Unlinks could happen here; completion reporting + * drops the lock. That's why fotg210->qh_scan_next + * always holds the next qh to scan; if the next qh + * gets unlinked then fotg210->qh_scan_next is adjusted + * in qh_unlink_periodic(). + */ + temp = qh_completions(fotg210, qh); + if (unlikely(qh->needs_rescan || + (list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED))) + start_unlink_intr(fotg210, qh); + else if (temp != 0) + goto rescan; + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* fotg210_iso_stream ops work with both ITD and SITD */ + +static struct fotg210_iso_stream * +iso_stream_alloc(gfp_t mem_flags) +{ + struct fotg210_iso_stream *stream; + + stream = kzalloc(sizeof(*stream), mem_flags); + if (likely(stream != NULL)) { + INIT_LIST_HEAD(&stream->td_list); + INIT_LIST_HEAD(&stream->free_list); + stream->next_uframe = -1; + } + return stream; +} + +static void +iso_stream_init( + struct fotg210_hcd *fotg210, + struct fotg210_iso_stream *stream, + struct usb_device *dev, + int pipe, + unsigned interval +) +{ + u32 buf1; + unsigned epnum, maxp; + int is_input; + long bandwidth; + unsigned multi; + + /* + * this might be a "high bandwidth" highspeed endpoint, + * as encoded in the ep descriptor's wMaxPacket field + */ + epnum = usb_pipeendpoint(pipe); + is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; + maxp = usb_maxpacket(dev, pipe, !is_input); + if (is_input) + buf1 = (1 << 11); + else + buf1 = 0; + + maxp = max_packet(maxp); + multi = hb_mult(maxp); + buf1 |= maxp; + maxp *= multi; + + stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); + stream->buf1 = cpu_to_hc32(fotg210, buf1); + stream->buf2 = cpu_to_hc32(fotg210, multi); + + /* usbfs wants to report the average usecs per frame tied up + * when transfers on this endpoint are scheduled ... + */ + if (dev->speed == USB_SPEED_FULL) { + interval <<= 3; + stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, + is_input, 1, maxp)); + stream->usecs /= 8; + } else { + stream->highspeed = 1; + stream->usecs = HS_USECS_ISO(maxp); + } + bandwidth = stream->usecs * 8; + bandwidth /= interval; + + stream->bandwidth = bandwidth; + stream->udev = dev; + stream->bEndpointAddress = is_input | epnum; + stream->interval = interval; + stream->maxp = maxp; +} + +static struct fotg210_iso_stream * +iso_stream_find(struct fotg210_hcd *fotg210, struct urb *urb) +{ + unsigned epnum; + struct fotg210_iso_stream *stream; + struct usb_host_endpoint *ep; + unsigned long flags; + + epnum = usb_pipeendpoint(urb->pipe); + if (usb_pipein(urb->pipe)) + ep = urb->dev->ep_in[epnum]; + else + ep = urb->dev->ep_out[epnum]; + + spin_lock_irqsave(&fotg210->lock, flags); + stream = ep->hcpriv; + + if (unlikely(stream == NULL)) { + stream = iso_stream_alloc(GFP_ATOMIC); + if (likely(stream != NULL)) { + ep->hcpriv = stream; + stream->ep = ep; + iso_stream_init(fotg210, stream, urb->dev, urb->pipe, + urb->interval); + } + + /* if dev->ep[epnum] is a QH, hw is set */ + } else if (unlikely(stream->hw != NULL)) { + fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n", + urb->dev->devpath, epnum, + usb_pipein(urb->pipe) ? "in" : "out"); + stream = NULL; + } + + spin_unlock_irqrestore(&fotg210->lock, flags); + return stream; +} + +/*-------------------------------------------------------------------------*/ + +/* fotg210_iso_sched ops can be ITD-only or SITD-only */ + +static struct fotg210_iso_sched * +iso_sched_alloc(unsigned packets, gfp_t mem_flags) +{ + struct fotg210_iso_sched *iso_sched; + int size = sizeof(*iso_sched); + + size += packets * sizeof(struct fotg210_iso_packet); + iso_sched = kzalloc(size, mem_flags); + if (likely(iso_sched != NULL)) + INIT_LIST_HEAD(&iso_sched->td_list); + + return iso_sched; +} + +static inline void +itd_sched_init( + struct fotg210_hcd *fotg210, + struct fotg210_iso_sched *iso_sched, + struct fotg210_iso_stream *stream, + struct urb *urb +) +{ + unsigned i; + dma_addr_t dma = urb->transfer_dma; + + /* how many uframes are needed for these transfers */ + iso_sched->span = urb->number_of_packets * stream->interval; + + /* figure out per-uframe itd fields that we'll need later + * when we fit new itds into the schedule. + */ + for (i = 0; i < urb->number_of_packets; i++) { + struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; + unsigned length; + dma_addr_t buf; + u32 trans; + + length = urb->iso_frame_desc[i].length; + buf = dma + urb->iso_frame_desc[i].offset; + + trans = FOTG210_ISOC_ACTIVE; + trans |= buf & 0x0fff; + if (unlikely(((i + 1) == urb->number_of_packets)) + && !(urb->transfer_flags & URB_NO_INTERRUPT)) + trans |= FOTG210_ITD_IOC; + trans |= length << 16; + uframe->transaction = cpu_to_hc32(fotg210, trans); + + /* might need to cross a buffer page within a uframe */ + uframe->bufp = (buf & ~(u64)0x0fff); + buf += length; + if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff)))) + uframe->cross = 1; + } +} + +static void +iso_sched_free( + struct fotg210_iso_stream *stream, + struct fotg210_iso_sched *iso_sched +) +{ + if (!iso_sched) + return; + /* caller must hold fotg210->lock!*/ + list_splice(&iso_sched->td_list, &stream->free_list); + kfree(iso_sched); +} + +static int +itd_urb_transaction( + struct fotg210_iso_stream *stream, + struct fotg210_hcd *fotg210, + struct urb *urb, + gfp_t mem_flags +) +{ + struct fotg210_itd *itd; + dma_addr_t itd_dma; + int i; + unsigned num_itds; + struct fotg210_iso_sched *sched; + unsigned long flags; + + sched = iso_sched_alloc(urb->number_of_packets, mem_flags); + if (unlikely(sched == NULL)) + return -ENOMEM; + + itd_sched_init(fotg210, sched, stream, urb); + + if (urb->interval < 8) + num_itds = 1 + (sched->span + 7) / 8; + else + num_itds = urb->number_of_packets; + + /* allocate/init ITDs */ + spin_lock_irqsave(&fotg210->lock, flags); + for (i = 0; i < num_itds; i++) { + + /* + * Use iTDs from the free list, but not iTDs that may + * still be in use by the hardware. + */ + if (likely(!list_empty(&stream->free_list))) { + itd = list_first_entry(&stream->free_list, + struct fotg210_itd, itd_list); + if (itd->frame == fotg210->now_frame) + goto alloc_itd; + list_del(&itd->itd_list); + itd_dma = itd->itd_dma; + } else { + alloc_itd: + spin_unlock_irqrestore(&fotg210->lock, flags); + itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, + &itd_dma); + spin_lock_irqsave(&fotg210->lock, flags); + if (!itd) { + iso_sched_free(stream, sched); + spin_unlock_irqrestore(&fotg210->lock, flags); + return -ENOMEM; + } + } + + memset(itd, 0, sizeof(*itd)); + itd->itd_dma = itd_dma; + list_add(&itd->itd_list, &sched->td_list); + } + spin_unlock_irqrestore(&fotg210->lock, flags); + + /* temporarily store schedule info in hcpriv */ + urb->hcpriv = sched; + urb->error_count = 0; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static inline int +itd_slot_ok( + struct fotg210_hcd *fotg210, + u32 mod, + u32 uframe, + u8 usecs, + u32 period +) +{ + uframe %= period; + do { + /* can't commit more than uframe_periodic_max usec */ + if (periodic_usecs(fotg210, uframe >> 3, uframe & 0x7) + > (fotg210->uframe_periodic_max - usecs)) + return 0; + + /* we know urb->interval is 2^N uframes */ + uframe += period; + } while (uframe < mod); + return 1; +} + +/* + * This scheduler plans almost as far into the future as it has actual + * periodic schedule slots. (Affected by TUNE_FLS, which defaults to + * "as small as possible" to be cache-friendlier.) That limits the size + * transfers you can stream reliably; avoid more than 64 msec per urb. + * Also avoid queue depths of less than fotg210's worst irq latency (affected + * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter, + * and other factors); or more than about 230 msec total (for portability, + * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler! + */ + +#define SCHEDULE_SLOP 80 /* microframes */ + +static int +iso_stream_schedule( + struct fotg210_hcd *fotg210, + struct urb *urb, + struct fotg210_iso_stream *stream +) +{ + u32 now, next, start, period, span; + int status; + unsigned mod = fotg210->periodic_size << 3; + struct fotg210_iso_sched *sched = urb->hcpriv; + + period = urb->interval; + span = sched->span; + + if (span > mod - SCHEDULE_SLOP) { + fotg210_dbg(fotg210, "iso request %p too long\n", urb); + status = -EFBIG; + goto fail; + } + + now = fotg210_read_frame_index(fotg210) & (mod - 1); + + /* Typical case: reuse current schedule, stream is still active. + * Hopefully there are no gaps from the host falling behind + * (irq delays etc), but if there are we'll take the next + * slot in the schedule, implicitly assuming URB_ISO_ASAP. + */ + if (likely(!list_empty(&stream->td_list))) { + u32 excess; + + /* For high speed devices, allow scheduling within the + * isochronous scheduling threshold. For full speed devices + * and Intel PCI-based controllers, don't (work around for + * Intel ICH9 bug). + */ + if (!stream->highspeed && fotg210->fs_i_thresh) + next = now + fotg210->i_thresh; + else + next = now; + + /* Fell behind (by up to twice the slop amount)? + * We decide based on the time of the last currently-scheduled + * slot, not the time of the next available slot. + */ + excess = (stream->next_uframe - period - next) & (mod - 1); + if (excess >= mod - 2 * SCHEDULE_SLOP) + start = next + excess - mod + period * + DIV_ROUND_UP(mod - excess, period); + else + start = next + excess + period; + if (start - now >= mod) { + fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", + urb, start - now - period, period, + mod); + status = -EFBIG; + goto fail; + } + } + + /* need to schedule; when's the next (u)frame we could start? + * this is bigger than fotg210->i_thresh allows; scheduling itself + * isn't free, the slop should handle reasonably slow cpus. it + * can also help high bandwidth if the dma and irq loads don't + * jump until after the queue is primed. + */ + else { + int done = 0; + start = SCHEDULE_SLOP + (now & ~0x07); + + /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ + + /* find a uframe slot with enough bandwidth. + * Early uframes are more precious because full-speed + * iso IN transfers can't use late uframes, + * and therefore they should be allocated last. + */ + next = start; + start += period; + do { + start--; + /* check schedule: enough space? */ + if (itd_slot_ok(fotg210, mod, start, + stream->usecs, period)) + done = 1; + } while (start > next && !done); + + /* no room in the schedule */ + if (!done) { + fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n", + urb, now, now + mod); + status = -ENOSPC; + goto fail; + } + } + + /* Tried to schedule too far into the future? */ + if (unlikely(start - now + span - period + >= mod - 2 * SCHEDULE_SLOP)) { + fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", + urb, start - now, span - period, + mod - 2 * SCHEDULE_SLOP); + status = -EFBIG; + goto fail; + } + + stream->next_uframe = start & (mod - 1); + + /* report high speed start in uframes; full speed, in frames */ + urb->start_frame = stream->next_uframe; + if (!stream->highspeed) + urb->start_frame >>= 3; + + /* Make sure scan_isoc() sees these */ + if (fotg210->isoc_count == 0) + fotg210->next_frame = now >> 3; + return 0; + + fail: + iso_sched_free(stream, sched); + urb->hcpriv = NULL; + return status; +} + +/*-------------------------------------------------------------------------*/ + +static inline void +itd_init(struct fotg210_hcd *fotg210, struct fotg210_iso_stream *stream, + struct fotg210_itd *itd) +{ + int i; + + /* it's been recently zeroed */ + itd->hw_next = FOTG210_LIST_END(fotg210); + itd->hw_bufp[0] = stream->buf0; + itd->hw_bufp[1] = stream->buf1; + itd->hw_bufp[2] = stream->buf2; + + for (i = 0; i < 8; i++) + itd->index[i] = -1; + + /* All other fields are filled when scheduling */ +} + +static inline void +itd_patch( + struct fotg210_hcd *fotg210, + struct fotg210_itd *itd, + struct fotg210_iso_sched *iso_sched, + unsigned index, + u16 uframe +) +{ + struct fotg210_iso_packet *uf = &iso_sched->packet[index]; + unsigned pg = itd->pg; + + uframe &= 0x07; + itd->index[uframe] = index; + + itd->hw_transaction[uframe] = uf->transaction; + itd->hw_transaction[uframe] |= cpu_to_hc32(fotg210, pg << 12); + itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, uf->bufp & ~(u32)0); + itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(uf->bufp >> 32)); + + /* iso_frame_desc[].offset must be strictly increasing */ + if (unlikely(uf->cross)) { + u64 bufp = uf->bufp + 4096; + + itd->pg = ++pg; + itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0); + itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(bufp >> 32)); + } +} + +static inline void +itd_link(struct fotg210_hcd *fotg210, unsigned frame, struct fotg210_itd *itd) +{ + union fotg210_shadow *prev = &fotg210->pshadow[frame]; + __hc32 *hw_p = &fotg210->periodic[frame]; + union fotg210_shadow here = *prev; + __hc32 type = 0; + + /* skip any iso nodes which might belong to previous microframes */ + while (here.ptr) { + type = Q_NEXT_TYPE(fotg210, *hw_p); + if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) + break; + prev = periodic_next_shadow(fotg210, prev, type); + hw_p = shadow_next_periodic(fotg210, &here, type); + here = *prev; + } + + itd->itd_next = here; + itd->hw_next = *hw_p; + prev->itd = itd; + itd->frame = frame; + wmb(); + *hw_p = cpu_to_hc32(fotg210, itd->itd_dma | Q_TYPE_ITD); +} + +/* fit urb's itds into the selected schedule slot; activate as needed */ +static void itd_link_urb( + struct fotg210_hcd *fotg210, + struct urb *urb, + unsigned mod, + struct fotg210_iso_stream *stream +) +{ + int packet; + unsigned next_uframe, uframe, frame; + struct fotg210_iso_sched *iso_sched = urb->hcpriv; + struct fotg210_itd *itd; + + next_uframe = stream->next_uframe & (mod - 1); + + if (unlikely(list_empty(&stream->td_list))) { + fotg210_to_hcd(fotg210)->self.bandwidth_allocated + += stream->bandwidth; + fotg210_vdbg(fotg210, + "schedule devp %s ep%d%s-iso period %d start %d.%d\n", + urb->dev->devpath, stream->bEndpointAddress & 0x0f, + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", + urb->interval, + next_uframe >> 3, next_uframe & 0x7); + } + + /* fill iTDs uframe by uframe */ + for (packet = 0, itd = NULL; packet < urb->number_of_packets;) { + if (itd == NULL) { + /* ASSERT: we have all necessary itds */ + + /* ASSERT: no itds for this endpoint in this uframe */ + + itd = list_entry(iso_sched->td_list.next, + struct fotg210_itd, itd_list); + list_move_tail(&itd->itd_list, &stream->td_list); + itd->stream = stream; + itd->urb = urb; + itd_init(fotg210, stream, itd); + } + + uframe = next_uframe & 0x07; + frame = next_uframe >> 3; + + itd_patch(fotg210, itd, iso_sched, packet, uframe); + + next_uframe += stream->interval; + next_uframe &= mod - 1; + packet++; + + /* link completed itds into the schedule */ + if (((next_uframe >> 3) != frame) + || packet == urb->number_of_packets) { + itd_link(fotg210, frame & (fotg210->periodic_size - 1), + itd); + itd = NULL; + } + } + stream->next_uframe = next_uframe; + + /* don't need that schedule data any more */ + iso_sched_free(stream, iso_sched); + urb->hcpriv = NULL; + + ++fotg210->isoc_count; + enable_periodic(fotg210); +} + +#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ + FOTG210_ISOC_XACTERR) + +/* Process and recycle a completed ITD. Return true iff its urb completed, + * and hence its completion callback probably added things to the hardware + * schedule. + * + * Note that we carefully avoid recycling this descriptor until after any + * completion callback runs, so that it won't be reused quickly. That is, + * assuming (a) no more than two urbs per frame on this endpoint, and also + * (b) only this endpoint's completions submit URBs. It seems some silicon + * corrupts things if you reuse completed descriptors very quickly... + */ +static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) +{ + struct urb *urb = itd->urb; + struct usb_iso_packet_descriptor *desc; + u32 t; + unsigned uframe; + int urb_index = -1; + struct fotg210_iso_stream *stream = itd->stream; + struct usb_device *dev; + bool retval = false; + + /* for each uframe with a packet */ + for (uframe = 0; uframe < 8; uframe++) { + if (likely(itd->index[uframe] == -1)) + continue; + urb_index = itd->index[uframe]; + desc = &urb->iso_frame_desc[urb_index]; + + t = hc32_to_cpup(fotg210, &itd->hw_transaction[uframe]); + itd->hw_transaction[uframe] = 0; + + /* report transfer status */ + if (unlikely(t & ISO_ERRS)) { + urb->error_count++; + if (t & FOTG210_ISOC_BUF_ERR) + desc->status = usb_pipein(urb->pipe) + ? -ENOSR /* hc couldn't read */ + : -ECOMM; /* hc couldn't write */ + else if (t & FOTG210_ISOC_BABBLE) + desc->status = -EOVERFLOW; + else /* (t & FOTG210_ISOC_XACTERR) */ + desc->status = -EPROTO; + + /* HC need not update length with this error */ + if (!(t & FOTG210_ISOC_BABBLE)) { + desc->actual_length = + fotg210_itdlen(urb, desc, t); + urb->actual_length += desc->actual_length; + } + } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { + desc->status = 0; + desc->actual_length = fotg210_itdlen(urb, desc, t); + urb->actual_length += desc->actual_length; + } else { + /* URB was too late */ + desc->status = -EXDEV; + } + } + + /* handle completion now? */ + if (likely((urb_index + 1) != urb->number_of_packets)) + goto done; + + /* ASSERT: it's really the last itd for this urb + list_for_each_entry (itd, &stream->td_list, itd_list) + BUG_ON (itd->urb == urb); + */ + + /* give urb back to the driver; completion often (re)submits */ + dev = urb->dev; + fotg210_urb_done(fotg210, urb, 0); + retval = true; + urb = NULL; + + --fotg210->isoc_count; + disable_periodic(fotg210); + + if (unlikely(list_is_singular(&stream->td_list))) { + fotg210_to_hcd(fotg210)->self.bandwidth_allocated + -= stream->bandwidth; + fotg210_vdbg(fotg210, + "deschedule devp %s ep%d%s-iso\n", + dev->devpath, stream->bEndpointAddress & 0x0f, + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); + } + +done: + itd->urb = NULL; + + /* Add to the end of the free list for later reuse */ + list_move_tail(&itd->itd_list, &stream->free_list); + + /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ + if (list_empty(&stream->td_list)) { + list_splice_tail_init(&stream->free_list, + &fotg210->cached_itd_list); + start_free_itds(fotg210); + } + + return retval; +} + +/*-------------------------------------------------------------------------*/ + +static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, + gfp_t mem_flags) +{ + int status = -EINVAL; + unsigned long flags; + struct fotg210_iso_stream *stream; + + /* Get iso_stream head */ + stream = iso_stream_find(fotg210, urb); + if (unlikely(stream == NULL)) { + fotg210_dbg(fotg210, "can't get iso stream\n"); + return -ENOMEM; + } + if (unlikely(urb->interval != stream->interval && + fotg210_port_speed(fotg210, 0) == + USB_PORT_STAT_HIGH_SPEED)) { + fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", + stream->interval, urb->interval); + goto done; + } + +#ifdef FOTG210_URB_TRACE + fotg210_dbg(fotg210, + "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", + __func__, urb->dev->devpath, urb, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->transfer_buffer_length, + urb->number_of_packets, urb->interval, + stream); +#endif + + /* allocate ITDs w/o locking anything */ + status = itd_urb_transaction(stream, fotg210, urb, mem_flags); + if (unlikely(status < 0)) { + fotg210_dbg(fotg210, "can't init itds\n"); + goto done; + } + + /* schedule ... need to lock */ + spin_lock_irqsave(&fotg210->lock, flags); + if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { + status = -ESHUTDOWN; + goto done_not_linked; + } + status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); + if (unlikely(status)) + goto done_not_linked; + status = iso_stream_schedule(fotg210, urb, stream); + if (likely(status == 0)) + itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream); + else + usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); + done_not_linked: + spin_unlock_irqrestore(&fotg210->lock, flags); + done: + return status; +} + +/*-------------------------------------------------------------------------*/ + +static void scan_isoc(struct fotg210_hcd *fotg210) +{ + unsigned uf, now_frame, frame; + unsigned fmask = fotg210->periodic_size - 1; + bool modified, live; + + /* + * When running, scan from last scan point up to "now" + * else clean up by scanning everything that's left. + * Touches as few pages as possible: cache-friendly. + */ + if (fotg210->rh_state >= FOTG210_RH_RUNNING) { + uf = fotg210_read_frame_index(fotg210); + now_frame = (uf >> 3) & fmask; + live = true; + } else { + now_frame = (fotg210->next_frame - 1) & fmask; + live = false; + } + fotg210->now_frame = now_frame; + + frame = fotg210->next_frame; + for (;;) { + union fotg210_shadow q, *q_p; + __hc32 type, *hw_p; + +restart: + /* scan each element in frame's queue for completions */ + q_p = &fotg210->pshadow[frame]; + hw_p = &fotg210->periodic[frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE(fotg210, *hw_p); + modified = false; + + while (q.ptr != NULL) { + switch (hc32_to_cpu(fotg210, type)) { + case Q_TYPE_ITD: + /* If this ITD is still active, leave it for + * later processing ... check the next entry. + * No need to check for activity unless the + * frame is current. + */ + if (frame == now_frame && live) { + rmb(); + for (uf = 0; uf < 8; uf++) { + if (q.itd->hw_transaction[uf] & + ITD_ACTIVE(fotg210)) + break; + } + if (uf < 8) { + q_p = &q.itd->itd_next; + hw_p = &q.itd->hw_next; + type = Q_NEXT_TYPE(fotg210, + q.itd->hw_next); + q = *q_p; + break; + } + } + + /* Take finished ITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. HC won't cache the + * pointer for much longer, if at all. + */ + *q_p = q.itd->itd_next; + *hw_p = q.itd->hw_next; + type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); + wmb(); + modified = itd_complete(fotg210, q.itd); + q = *q_p; + break; + default: + fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", + type, frame, q.ptr); + /* FALL THROUGH */ + case Q_TYPE_QH: + case Q_TYPE_FSTN: + /* End of the iTDs and siTDs */ + q.ptr = NULL; + break; + } + + /* assume completion callbacks modify the queue */ + if (unlikely(modified && fotg210->isoc_count > 0)) + goto restart; + } + + /* Stop when we have reached the current frame */ + if (frame == now_frame) + break; + frame = (frame + 1) & fmask; + } + fotg210->next_frame = now_frame; +} +/*-------------------------------------------------------------------------*/ +/* + * Display / Set uframe_periodic_max + */ +static ssize_t show_uframe_periodic_max(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fotg210_hcd *fotg210; + int n; + + fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); + n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); + return n; +} + + +static ssize_t store_uframe_periodic_max(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fotg210_hcd *fotg210; + unsigned uframe_periodic_max; + unsigned frame, uframe; + unsigned short allocated_max; + unsigned long flags; + ssize_t ret; + + fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); + if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) + return -EINVAL; + + if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { + fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n", + uframe_periodic_max); + return -EINVAL; + } + + ret = -EINVAL; + + /* + * lock, so that our checking does not race with possible periodic + * bandwidth allocation through submitting new urbs. + */ + spin_lock_irqsave(&fotg210->lock, flags); + + /* + * for request to decrease max periodic bandwidth, we have to check + * every microframe in the schedule to see whether the decrease is + * possible. + */ + if (uframe_periodic_max < fotg210->uframe_periodic_max) { + allocated_max = 0; + + for (frame = 0; frame < fotg210->periodic_size; ++frame) + for (uframe = 0; uframe < 7; ++uframe) + allocated_max = max(allocated_max, + periodic_usecs(fotg210, frame, uframe)); + + if (allocated_max > uframe_periodic_max) { + fotg210_info(fotg210, + "cannot decrease uframe_periodic_max becase " + "periodic bandwidth is already allocated " + "(%u > %u)\n", + allocated_max, uframe_periodic_max); + goto out_unlock; + } + } + + /* increasing is always ok */ + + fotg210_info(fotg210, "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", + 100 * uframe_periodic_max/125, uframe_periodic_max); + + if (uframe_periodic_max != 100) + fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n"); + + fotg210->uframe_periodic_max = uframe_periodic_max; + ret = count; + +out_unlock: + spin_unlock_irqrestore(&fotg210->lock, flags); + return ret; +} + +static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, + store_uframe_periodic_max); + +static inline int create_sysfs_files(struct fotg210_hcd *fotg210) +{ + struct device *controller = fotg210_to_hcd(fotg210)->self.controller; + int i = 0; + + if (i) + goto out; + + i = device_create_file(controller, &dev_attr_uframe_periodic_max); +out: + return i; +} + +static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) +{ + struct device *controller = fotg210_to_hcd(fotg210)->self.controller; + + device_remove_file(controller, &dev_attr_uframe_periodic_max); +} +/*-------------------------------------------------------------------------*/ + +/* On some systems, leaving remote wakeup enabled prevents system shutdown. + * The firmware seems to think that powering off is a wakeup event! + * This routine turns off remote wakeup and everything else, on all ports. + */ +static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210) +{ + u32 __iomem *status_reg = &fotg210->regs->port_status; + + fotg210_writel(fotg210, PORT_RWC_BITS, status_reg); +} + +/* + * Halt HC, turn off all ports, and let the BIOS use the companion controllers. + * Must be called with interrupts enabled and the lock not held. + */ +static void fotg210_silence_controller(struct fotg210_hcd *fotg210) +{ + fotg210_halt(fotg210); + + spin_lock_irq(&fotg210->lock); + fotg210->rh_state = FOTG210_RH_HALTED; + fotg210_turn_off_all_ports(fotg210); + spin_unlock_irq(&fotg210->lock); +} + +/* fotg210_shutdown kick in for silicon on any bus (not just pci, etc). + * This forcibly disables dma and IRQs, helping kexec and other cases + * where the next system software may expect clean state. + */ +static void fotg210_shutdown(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + + spin_lock_irq(&fotg210->lock); + fotg210->shutdown = true; + fotg210->rh_state = FOTG210_RH_STOPPING; + fotg210->enabled_hrtimer_events = 0; + spin_unlock_irq(&fotg210->lock); + + fotg210_silence_controller(fotg210); + + hrtimer_cancel(&fotg210->hrtimer); +} + +/*-------------------------------------------------------------------------*/ + +/* + * fotg210_work is called from some interrupts, timers, and so on. + * it calls driver completion functions, after dropping fotg210->lock. + */ +static void fotg210_work(struct fotg210_hcd *fotg210) +{ + /* another CPU may drop fotg210->lock during a schedule scan while + * it reports urb completions. this flag guards against bogus + * attempts at re-entrant schedule scanning. + */ + if (fotg210->scanning) { + fotg210->need_rescan = true; + return; + } + fotg210->scanning = true; + + rescan: + fotg210->need_rescan = false; + if (fotg210->async_count) + scan_async(fotg210); + if (fotg210->intr_count > 0) + scan_intr(fotg210); + if (fotg210->isoc_count > 0) + scan_isoc(fotg210); + if (fotg210->need_rescan) + goto rescan; + fotg210->scanning = false; + + /* the IO watchdog guards against hardware or driver bugs that + * misplace IRQs, and should let us run completely without IRQs. + * such lossage has been observed on both VT6202 and VT8235. + */ + turn_on_io_watchdog(fotg210); +} + +/* + * Called when the fotg210_hcd module is removed. + */ +static void fotg210_stop(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + + fotg210_dbg(fotg210, "stop\n"); + + /* no more interrupts ... */ + + spin_lock_irq(&fotg210->lock); + fotg210->enabled_hrtimer_events = 0; + spin_unlock_irq(&fotg210->lock); + + fotg210_quiesce(fotg210); + fotg210_silence_controller(fotg210); + fotg210_reset(fotg210); + + hrtimer_cancel(&fotg210->hrtimer); + remove_sysfs_files(fotg210); + remove_debug_files(fotg210); + + /* root hub is shut down separately (first, when possible) */ + spin_lock_irq(&fotg210->lock); + end_free_itds(fotg210); + spin_unlock_irq(&fotg210->lock); + fotg210_mem_cleanup(fotg210); + +#ifdef FOTG210_STATS + fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n", + fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa, + fotg210->stats.lost_iaa); + fotg210_dbg(fotg210, "complete %ld unlink %ld\n", + fotg210->stats.complete, fotg210->stats.unlink); +#endif + + dbg_status(fotg210, "fotg210_stop completed", + fotg210_readl(fotg210, &fotg210->regs->status)); +} + +/* one-time init, only for memory state */ +static int hcd_fotg210_init(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp; + int retval; + u32 hcc_params; + struct fotg210_qh_hw *hw; + + spin_lock_init(&fotg210->lock); + + /* + * keep io watchdog by default, those good HCDs could turn off it later + */ + fotg210->need_io_watchdog = 1; + + hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + fotg210->hrtimer.function = fotg210_hrtimer_func; + fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; + + hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); + + /* + * by default set standard 80% (== 100 usec/uframe) max periodic + * bandwidth as required by USB 2.0 + */ + fotg210->uframe_periodic_max = 100; + + /* + * hw default: 1K periodic list heads, one per frame. + * periodic_size can shrink by USBCMD update if hcc_params allows. + */ + fotg210->periodic_size = DEFAULT_I_TDPS; + INIT_LIST_HEAD(&fotg210->intr_qh_list); + INIT_LIST_HEAD(&fotg210->cached_itd_list); + + if (HCC_PGM_FRAMELISTLEN(hcc_params)) { + /* periodic schedule size can be smaller than default */ + switch (FOTG210_TUNE_FLS) { + case 0: + fotg210->periodic_size = 1024; + break; + case 1: + fotg210->periodic_size = 512; + break; + case 2: + fotg210->periodic_size = 256; + break; + default: + BUG(); + } + } + retval = fotg210_mem_init(fotg210, GFP_KERNEL); + if (retval < 0) + return retval; + + /* controllers may cache some of the periodic schedule ... */ + fotg210->i_thresh = 2; + + /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * its dummy is used in hw_alt_next of many tds, to prevent the qh + * from automatically advancing to the next td after short reads. + */ + fotg210->async->qh_next.qh = NULL; + hw = fotg210->async->hw; + hw->hw_next = QH_NEXT(fotg210, fotg210->async->qh_dma); + hw->hw_info1 = cpu_to_hc32(fotg210, QH_HEAD); + hw->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); + hw->hw_qtd_next = FOTG210_LIST_END(fotg210); + fotg210->async->qh_state = QH_STATE_LINKED; + hw->hw_alt_next = QTD_NEXT(fotg210, fotg210->async->dummy->qtd_dma); + + /* clear interrupt enables, set irq latency */ + if (log2_irq_thresh < 0 || log2_irq_thresh > 6) + log2_irq_thresh = 0; + temp = 1 << (16 + log2_irq_thresh); + if (HCC_CANPARK(hcc_params)) { + /* HW default park == 3, on hardware that supports it (like + * NVidia and ALI silicon), maximizes throughput on the async + * schedule by avoiding QH fetches between transfers. + * + * With fast usb storage devices and NForce2, "park" seems to + * make problems: throughput reduction (!), data errors... + */ + if (park) { + park = min_t(unsigned, park, 3); + temp |= CMD_PARK; + temp |= park << 8; + } + fotg210_dbg(fotg210, "park %d\n", park); + } + if (HCC_PGM_FRAMELISTLEN(hcc_params)) { + /* periodic schedule size can be smaller than default */ + temp &= ~(3 << 2); + temp |= (FOTG210_TUNE_FLS << 2); + } + fotg210->command = temp; + + /* Accept arbitrarily long scatter-gather lists */ + if (!(hcd->driver->flags & HCD_LOCAL_MEM)) + hcd->self.sg_tablesize = ~0; + return 0; +} + +/* start HC running; it's halted, hcd_fotg210_init() has been run (once) */ +static int fotg210_run(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp; + u32 hcc_params; + + hcd->uses_new_polling = 1; + + /* EHCI spec section 4.1 */ + + fotg210_writel(fotg210, fotg210->periodic_dma, + &fotg210->regs->frame_list); + fotg210_writel(fotg210, (u32)fotg210->async->qh_dma, + &fotg210->regs->async_next); + + /* + * hcc_params controls whether fotg210->regs->segment must (!!!) + * be used; it constrains QH/ITD/SITD and QTD locations. + * pci_pool consistent memory always uses segment zero. + * streaming mappings for I/O buffers, like pci_map_single(), + * can return segments above 4GB, if the device allows. + * + * NOTE: the dma mask is visible through dma_supported(), so + * drivers can pass this info along ... like NETIF_F_HIGHDMA, + * Scsi_Host.highmem_io, and so forth. It's readonly to all + * host side drivers though. + */ + hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); + + /* + * Philips, Intel, and maybe others need CMD_RUN before the + * root hub will detect new devices (why?); NEC doesn't + */ + fotg210->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + fotg210->command |= CMD_RUN; + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); + dbg_cmd(fotg210, "init", fotg210->command); + + /* + * Start, enabling full USB 2.0 functionality ... usb 1.1 devices + * are explicitly handed to companion controller(s), so no TT is + * involved with the root hub. (Except where one is integrated, + * and there's no companion controller unless maybe for USB OTG.) + * + * Turning on the CF flag will transfer ownership of all ports + * from the companions to the EHCI controller. If any of the + * companions are in the middle of a port reset at the time, it + * could cause trouble. Write-locking ehci_cf_port_reset_rwsem + * guarantees that no resets are in progress. After we set CF, + * a short delay lets the hardware catch up; new resets shouldn't + * be started before the port switching actions could complete. + */ + down_write(&ehci_cf_port_reset_rwsem); + fotg210->rh_state = FOTG210_RH_RUNNING; + /* unblock posted writes */ + fotg210_readl(fotg210, &fotg210->regs->command); + msleep(5); + up_write(&ehci_cf_port_reset_rwsem); + fotg210->last_periodic_enable = ktime_get_real(); + + temp = HC_VERSION(fotg210, + fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); + fotg210_info(fotg210, + "USB %x.%x started, EHCI %x.%02x\n", + ((fotg210->sbrn & 0xf0)>>4), (fotg210->sbrn & 0x0f), + temp >> 8, temp & 0xff); + + fotg210_writel(fotg210, INTR_MASK, + &fotg210->regs->intr_enable); /* Turn On Interrupts */ + + /* GRR this is run-once init(), being done every time the HC starts. + * So long as they're part of class devices, we can't do it init() + * since the class device isn't created that early. + */ + create_debug_files(fotg210); + create_sysfs_files(fotg210); + + return 0; +} + +static int fotg210_setup(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + int retval; + + fotg210->regs = (void __iomem *)fotg210->caps + + HC_LENGTH(fotg210, + fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); + dbg_hcs_params(fotg210, "reset"); + dbg_hcc_params(fotg210, "reset"); + + /* cache this readonly data; minimize chip reads */ + fotg210->hcs_params = fotg210_readl(fotg210, + &fotg210->caps->hcs_params); + + fotg210->sbrn = HCD_USB2; + + /* data structure init */ + retval = hcd_fotg210_init(hcd); + if (retval) + return retval; + + retval = fotg210_halt(fotg210); + if (retval) + return retval; + + fotg210_reset(fotg210); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static irqreturn_t fotg210_irq(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 status, masked_status, pcd_status = 0, cmd; + int bh; + + spin_lock(&fotg210->lock); + + status = fotg210_readl(fotg210, &fotg210->regs->status); + + /* e.g. cardbus physical eject */ + if (status == ~(u32) 0) { + fotg210_dbg(fotg210, "device removed\n"); + goto dead; + } + + /* + * We don't use STS_FLR, but some controllers don't like it to + * remain on, so mask it out along with the other status bits. + */ + masked_status = status & (INTR_MASK | STS_FLR); + + /* Shared IRQ? */ + if (!masked_status || + unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { + spin_unlock(&fotg210->lock); + return IRQ_NONE; + } + + /* clear (just) interrupts */ + fotg210_writel(fotg210, masked_status, &fotg210->regs->status); + cmd = fotg210_readl(fotg210, &fotg210->regs->command); + bh = 0; + +#ifdef VERBOSE_DEBUG + /* unrequested/ignored: Frame List Rollover */ + dbg_status(fotg210, "irq", status); +#endif + + /* INT, ERR, and IAA interrupt rates can be throttled */ + + /* normal [4.15.1.2] or error [4.15.1.1] completion */ + if (likely((status & (STS_INT|STS_ERR)) != 0)) { + if (likely((status & STS_ERR) == 0)) + COUNT(fotg210->stats.normal); + else + COUNT(fotg210->stats.error); + bh = 1; + } + + /* complete the unlinking of some qh [4.15.2.3] */ + if (status & STS_IAA) { + + /* Turn off the IAA watchdog */ + fotg210->enabled_hrtimer_events &= + ~BIT(FOTG210_HRTIMER_IAA_WATCHDOG); + + /* + * Mild optimization: Allow another IAAD to reset the + * hrtimer, if one occurs before the next expiration. + * In theory we could always cancel the hrtimer, but + * tests show that about half the time it will be reset + * for some other event anyway. + */ + if (fotg210->next_hrtimer_event == FOTG210_HRTIMER_IAA_WATCHDOG) + ++fotg210->next_hrtimer_event; + + /* guard against (alleged) silicon errata */ + if (cmd & CMD_IAAD) + fotg210_dbg(fotg210, "IAA with IAAD still set?\n"); + if (fotg210->async_iaa) { + COUNT(fotg210->stats.iaa); + end_unlink_async(fotg210); + } else + fotg210_dbg(fotg210, "IAA with nothing unlinked?\n"); + } + + /* remote wakeup [4.3.1] */ + if (status & STS_PCD) { + int pstatus; + u32 __iomem *status_reg = &fotg210->regs->port_status; + + /* kick root hub later */ + pcd_status = status; + + /* resume root hub? */ + if (fotg210->rh_state == FOTG210_RH_SUSPENDED) + usb_hcd_resume_root_hub(hcd); + + pstatus = fotg210_readl(fotg210, status_reg); + + if (test_bit(0, &fotg210->suspended_ports) && + ((pstatus & PORT_RESUME) || + !(pstatus & PORT_SUSPEND)) && + (pstatus & PORT_PE) && + fotg210->reset_done[0] == 0) { + + /* start 20 msec resume signaling from this port, + * and make khubd collect PORT_STAT_C_SUSPEND to + * stop that signaling. Use 5 ms extra for safety, + * like usb_port_resume() does. + */ + fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25); + set_bit(0, &fotg210->resuming_ports); + fotg210_dbg(fotg210, "port 1 remote wakeup\n"); + mod_timer(&hcd->rh_timer, fotg210->reset_done[0]); + } + } + + /* PCI errors [4.15.2.4] */ + if (unlikely((status & STS_FATAL) != 0)) { + fotg210_err(fotg210, "fatal error\n"); + dbg_cmd(fotg210, "fatal", cmd); + dbg_status(fotg210, "fatal", status); +dead: + usb_hc_died(hcd); + + /* Don't let the controller do anything more */ + fotg210->shutdown = true; + fotg210->rh_state = FOTG210_RH_STOPPING; + fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); + fotg210_writel(fotg210, fotg210->command, + &fotg210->regs->command); + fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); + fotg210_handle_controller_death(fotg210); + + /* Handle completions when the controller stops */ + bh = 0; + } + + if (bh) + fotg210_work(fotg210); + spin_unlock(&fotg210->lock); + if (pcd_status) + usb_hcd_poll_rh_status(hcd); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +/* + * non-error returns are a promise to giveback() the urb later + * we drop ownership so next owner (or urb unlink) can get it + * + * urb + dev is in hcd.self.controller.urb_list + * we're queueing TDs onto software and hardware lists + * + * hcd-specific init for hcpriv hasn't been done yet + * + * NOTE: control, bulk, and interrupt share the same code to append TDs + * to a (possibly active) QH, and the same QH scanning code. + */ +static int fotg210_urb_enqueue( + struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags +) { + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct list_head qtd_list; + + INIT_LIST_HEAD(&qtd_list); + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + /* qh_completions() code doesn't handle all the fault cases + * in multi-TD control transfers. Even 1KB is rare anyway. + */ + if (urb->transfer_buffer_length > (16 * 1024)) + return -EMSGSIZE; + /* FALLTHROUGH */ + /* case PIPE_BULK: */ + default: + if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return submit_async(fotg210, urb, &qtd_list, mem_flags); + + case PIPE_INTERRUPT: + if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return intr_submit(fotg210, urb, &qtd_list, mem_flags); + + case PIPE_ISOCHRONOUS: + return itd_submit(fotg210, urb, mem_flags); + } +} + +/* remove from hardware lists + * completions normally happen asynchronously + */ + +static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_qh *qh; + unsigned long flags; + int rc; + + spin_lock_irqsave(&fotg210->lock, flags); + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) + goto done; + + switch (usb_pipetype(urb->pipe)) { + /* case PIPE_CONTROL: */ + /* case PIPE_BULK:*/ + default: + qh = (struct fotg210_qh *) urb->hcpriv; + if (!qh) + break; + switch (qh->qh_state) { + case QH_STATE_LINKED: + case QH_STATE_COMPLETING: + start_unlink_async(fotg210, qh); + break; + case QH_STATE_UNLINK: + case QH_STATE_UNLINK_WAIT: + /* already started */ + break; + case QH_STATE_IDLE: + /* QH might be waiting for a Clear-TT-Buffer */ + qh_completions(fotg210, qh); + break; + } + break; + + case PIPE_INTERRUPT: + qh = (struct fotg210_qh *) urb->hcpriv; + if (!qh) + break; + switch (qh->qh_state) { + case QH_STATE_LINKED: + case QH_STATE_COMPLETING: + start_unlink_intr(fotg210, qh); + break; + case QH_STATE_IDLE: + qh_completions(fotg210, qh); + break; + default: + fotg210_dbg(fotg210, "bogus qh %p state %d\n", + qh, qh->qh_state); + goto done; + } + break; + + case PIPE_ISOCHRONOUS: + /* itd... */ + + /* wait till next completion, do it then. */ + /* completion irqs can wait up to 1024 msec, */ + break; + } +done: + spin_unlock_irqrestore(&fotg210->lock, flags); + return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* bulk qh holds the data toggle */ + +static void +fotg210_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + unsigned long flags; + struct fotg210_qh *qh, *tmp; + + /* ASSERT: any requests/urbs are being unlinked */ + /* ASSERT: nobody can be submitting urbs for this any more */ + +rescan: + spin_lock_irqsave(&fotg210->lock, flags); + qh = ep->hcpriv; + if (!qh) + goto done; + + /* endpoints can be iso streams. for now, we don't + * accelerate iso completions ... so spin a while. + */ + if (qh->hw == NULL) { + struct fotg210_iso_stream *stream = ep->hcpriv; + + if (!list_empty(&stream->td_list)) + goto idle_timeout; + + /* BUG_ON(!list_empty(&stream->free_list)); */ + kfree(stream); + goto done; + } + + if (fotg210->rh_state < FOTG210_RH_RUNNING) + qh->qh_state = QH_STATE_IDLE; + switch (qh->qh_state) { + case QH_STATE_LINKED: + case QH_STATE_COMPLETING: + for (tmp = fotg210->async->qh_next.qh; + tmp && tmp != qh; + tmp = tmp->qh_next.qh) + continue; + /* periodic qh self-unlinks on empty, and a COMPLETING qh + * may already be unlinked. + */ + if (tmp) + start_unlink_async(fotg210, qh); + /* FALL THROUGH */ + case QH_STATE_UNLINK: /* wait for hw to finish? */ + case QH_STATE_UNLINK_WAIT: +idle_timeout: + spin_unlock_irqrestore(&fotg210->lock, flags); + schedule_timeout_uninterruptible(1); + goto rescan; + case QH_STATE_IDLE: /* fully unlinked */ + if (qh->clearing_tt) + goto idle_timeout; + if (list_empty(&qh->qtd_list)) { + qh_destroy(fotg210, qh); + break; + } + /* else FALL THROUGH */ + default: + /* caller was supposed to have unlinked any requests; + * that's not our job. just leak this memory. + */ + fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n", + qh, ep->desc.bEndpointAddress, qh->qh_state, + list_empty(&qh->qtd_list) ? "" : "(has tds)"); + break; + } + done: + ep->hcpriv = NULL; + spin_unlock_irqrestore(&fotg210->lock, flags); +} + +static void +fotg210_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_qh *qh; + int eptype = usb_endpoint_type(&ep->desc); + int epnum = usb_endpoint_num(&ep->desc); + int is_out = usb_endpoint_dir_out(&ep->desc); + unsigned long flags; + + if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) + return; + + spin_lock_irqsave(&fotg210->lock, flags); + qh = ep->hcpriv; + + /* For Bulk and Interrupt endpoints we maintain the toggle state + * in the hardware; the toggle bits in udev aren't used at all. + * When an endpoint is reset by usb_clear_halt() we must reset + * the toggle bit in the QH. + */ + if (qh) { + usb_settoggle(qh->dev, epnum, is_out, 0); + if (!list_empty(&qh->qtd_list)) { + WARN_ONCE(1, "clear_halt for a busy endpoint\n"); + } else if (qh->qh_state == QH_STATE_LINKED || + qh->qh_state == QH_STATE_COMPLETING) { + + /* The toggle value in the QH can't be updated + * while the QH is active. Unlink it now; + * re-linking will call qh_refresh(). + */ + if (eptype == USB_ENDPOINT_XFER_BULK) + start_unlink_async(fotg210, qh); + else + start_unlink_intr(fotg210, qh); + } + } + spin_unlock_irqrestore(&fotg210->lock, flags); +} + +static int fotg210_get_frame(struct usb_hcd *hcd) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + return (fotg210_read_frame_index(fotg210) >> 3) % + fotg210->periodic_size; +} + +/*-------------------------------------------------------------------------*/ + +/* + * The EHCI in ChipIdea HDRC cannot be a separate module or device, + * because its registers (and irq) are shared between host/gadget/otg + * functions and in order to facilitate role switching we cannot + * give the fotg210 driver exclusive access to those. + */ +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); + +static const struct hc_driver fotg210_fotg210_hc_driver = { + .description = hcd_name, + .product_desc = "Faraday USB2.0 Host Controller", + .hcd_priv_size = sizeof(struct fotg210_hcd), + + /* + * generic hardware linkage + */ + .irq = fotg210_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = hcd_fotg210_init, + .start = fotg210_run, + .stop = fotg210_stop, + .shutdown = fotg210_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = fotg210_urb_enqueue, + .urb_dequeue = fotg210_urb_dequeue, + .endpoint_disable = fotg210_endpoint_disable, + .endpoint_reset = fotg210_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = fotg210_get_frame, + + /* + * root hub support + */ + .hub_status_data = fotg210_hub_status_data, + .hub_control = fotg210_hub_control, + .bus_suspend = fotg210_bus_suspend, + .bus_resume = fotg210_bus_resume, + + .relinquish_port = fotg210_relinquish_port, + .port_handed_over = fotg210_port_handed_over, + + .clear_tt_buffer_complete = fotg210_clear_tt_buffer_complete, +}; + +static void fotg210_init(struct fotg210_hcd *fotg210) +{ + u32 value; + + iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, + &fotg210->regs->gmir); + + value = ioread32(&fotg210->regs->otgcsr); + value &= ~OTGCSR_A_BUS_DROP; + value |= OTGCSR_A_BUS_REQ; + iowrite32(value, &fotg210->regs->otgcsr); +} + +/** + * fotg210_hcd_probe - initialize faraday FOTG210 HCDs + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +static int fotg210_hcd_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; + struct resource *res; + int irq; + int retval = -ENODEV; + struct fotg210_hcd *fotg210; + + if (usb_disabled()) + return -ENODEV; + + pdev->dev.power.power_state = PMSG_ON; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, + "Found HC with no IRQ. Check %s setup!\n", + dev_name(dev)); + return -ENODEV; + } + + irq = res->start; + + hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, + dev_name(dev)); + if (!hcd) { + dev_err(dev, "failed to create hcd with err %d\n", retval); + retval = -ENOMEM; + goto fail_create_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(dev)); + retval = -ENODEV; + goto fail_request_resource; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->has_tt = 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + fotg210_fotg210_hc_driver.description)) { + dev_dbg(dev, "controller already in use\n"); + retval = -EBUSY; + goto fail_request_resource; + } + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(dev)); + retval = -ENODEV; + goto fail_request_resource; + } + + hcd->regs = ioremap_nocache(res->start, resource_size(res)); + if (hcd->regs == NULL) { + dev_dbg(dev, "error mapping memory\n"); + retval = -EFAULT; + goto fail_ioremap; + } + + fotg210 = hcd_to_fotg210(hcd); + + fotg210->caps = hcd->regs; + + retval = fotg210_setup(hcd); + if (retval) + goto fail_add_hcd; + + fotg210_init(fotg210); + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval) { + dev_err(dev, "failed to add hcd with err %d\n", retval); + goto fail_add_hcd; + } + + return retval; + +fail_add_hcd: + iounmap(hcd->regs); +fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +fail_request_resource: + usb_put_hcd(hcd); +fail_create_hcd: + dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); + return retval; +} + +/** + * fotg210_hcd_remove - shutdown processing for EHCI HCDs + * @dev: USB Host Controller being removed + * + */ +static int fotg210_hcd_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); + + if (!hcd) + return 0; + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + return 0; +} + +static struct platform_driver fotg210_hcd_driver = { + .driver = { + .name = "fotg210-hcd", + }, + .probe = fotg210_hcd_probe, + .remove = fotg210_hcd_remove, +}; + +static int __init fotg210_hcd_init(void) +{ + int retval = 0; + + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); + if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || + test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) + pr_warn(KERN_WARNING "Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); + + pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd\n", + hcd_name, + sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd), + sizeof(struct fotg210_itd)); + +#ifdef DEBUG + fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); + if (!fotg210_debug_root) { + retval = -ENOENT; + goto err_debug; + } +#endif + + retval = platform_driver_register(&fotg210_hcd_driver); + if (retval < 0) + goto clean; + return retval; + + platform_driver_unregister(&fotg210_hcd_driver); +clean: +#ifdef DEBUG + debugfs_remove(fotg210_debug_root); + fotg210_debug_root = NULL; +err_debug: +#endif + clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); + return retval; +} +module_init(fotg210_hcd_init); + +static void __exit fotg210_hcd_cleanup(void) +{ + platform_driver_unregister(&fotg210_hcd_driver); +#ifdef DEBUG + debugfs_remove(fotg210_debug_root); +#endif + clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); +} +module_exit(fotg210_hcd_cleanup); diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h new file mode 100644 index 000000000000..8920f9d32564 --- /dev/null +++ b/drivers/usb/host/fotg210.h @@ -0,0 +1,750 @@ +#ifndef __LINUX_FOTG210_H +#define __LINUX_FOTG210_H + +/* definitions used for the EHCI driver */ + +/* + * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to + * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on + * the host controller implementation. + * + * To facilitate the strongest possible byte-order checking from "sparse" + * and so on, we use __leXX unless that's not practical. + */ +#define __hc32 __le32 +#define __hc16 __le16 + +/* statistics can be kept for tuning/monitoring */ +struct fotg210_stats { + /* irq usage */ + unsigned long normal; + unsigned long error; + unsigned long iaa; + unsigned long lost_iaa; + + /* termination of urbs from core */ + unsigned long complete; + unsigned long unlink; +}; + +/* fotg210_hcd->lock guards shared data against other CPUs: + * fotg210_hcd: async, unlink, periodic (and shadow), ... + * usb_host_endpoint: hcpriv + * fotg210_qh: qh_next, qtd_list + * fotg210_qtd: qtd_list + * + * Also, hold this lock when talking to HC registers or + * when updating hw_* fields in shared qh/qtd/... structures. + */ + +#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ + +/* + * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the + * controller may be doing DMA. Lower values mean there's no DMA. + */ +enum fotg210_rh_state { + FOTG210_RH_HALTED, + FOTG210_RH_SUSPENDED, + FOTG210_RH_RUNNING, + FOTG210_RH_STOPPING +}; + +/* + * Timer events, ordered by increasing delay length. + * Always update event_delays_ns[] and event_handlers[] (defined in + * ehci-timer.c) in parallel with this list. + */ +enum fotg210_hrtimer_event { + FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */ + FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ + FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ + FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ + FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ + FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ + FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ + FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ + FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ + FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ + FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ +}; +#define FOTG210_HRTIMER_NO_EVENT 99 + +struct fotg210_hcd { /* one per controller */ + /* timing support */ + enum fotg210_hrtimer_event next_hrtimer_event; + unsigned enabled_hrtimer_events; + ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS]; + struct hrtimer hrtimer; + + int PSS_poll_count; + int ASS_poll_count; + int died_poll_count; + + /* glue to PCI and HCD framework */ + struct fotg210_caps __iomem *caps; + struct fotg210_regs __iomem *regs; + struct fotg210_dbg_port __iomem *debug; + + __u32 hcs_params; /* cached register copy */ + spinlock_t lock; + enum fotg210_rh_state rh_state; + + /* general schedule support */ + bool scanning:1; + bool need_rescan:1; + bool intr_unlinking:1; + bool async_unlinking:1; + bool shutdown:1; + struct fotg210_qh *qh_scan_next; + + /* async schedule support */ + struct fotg210_qh *async; + struct fotg210_qh *dummy; /* For AMD quirk use */ + struct fotg210_qh *async_unlink; + struct fotg210_qh *async_unlink_last; + struct fotg210_qh *async_iaa; + unsigned async_unlink_cycle; + unsigned async_count; /* async activity count */ + + /* periodic schedule support */ +#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ + unsigned periodic_size; + __hc32 *periodic; /* hw periodic table */ + dma_addr_t periodic_dma; + struct list_head intr_qh_list; + unsigned i_thresh; /* uframes HC might cache */ + + union fotg210_shadow *pshadow; /* mirror hw periodic table */ + struct fotg210_qh *intr_unlink; + struct fotg210_qh *intr_unlink_last; + unsigned intr_unlink_cycle; + unsigned now_frame; /* frame from HC hardware */ + unsigned next_frame; /* scan periodic, start here */ + unsigned intr_count; /* intr activity count */ + unsigned isoc_count; /* isoc activity count */ + unsigned periodic_count; /* periodic activity count */ + /* max periodic time per uframe */ + unsigned uframe_periodic_max; + + + /* list of itds completed while now_frame was still active */ + struct list_head cached_itd_list; + struct fotg210_itd *last_itd_to_free; + + /* per root hub port */ + unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; + + /* bit vectors (one bit per port) */ + unsigned long bus_suspended; /* which ports were + already suspended at the start of a bus suspend */ + unsigned long companion_ports; /* which ports are + dedicated to the companion controller */ + unsigned long owned_ports; /* which ports are + owned by the companion during a bus suspend */ + unsigned long port_c_suspend; /* which ports have + the change-suspend feature turned on */ + unsigned long suspended_ports; /* which ports are + suspended */ + unsigned long resuming_ports; /* which ports have + started to resume */ + + /* per-HC memory pools (could be per-bus, but ...) */ + struct dma_pool *qh_pool; /* qh per active urb */ + struct dma_pool *qtd_pool; /* one or more per qh */ + struct dma_pool *itd_pool; /* itd per iso urb */ + + unsigned random_frame; + unsigned long next_statechange; + ktime_t last_periodic_enable; + u32 command; + + /* SILICON QUIRKS */ + unsigned need_io_watchdog:1; + unsigned fs_i_thresh:1; /* Intel iso scheduling */ + + u8 sbrn; /* packed release number */ + + /* irq statistics */ +#ifdef FOTG210_STATS + struct fotg210_stats stats; +# define COUNT(x) ((x)++) +#else +# define COUNT(x) +#endif + + /* debug files */ +#ifdef DEBUG + struct dentry *debug_dir; +#endif +}; + +/* convert between an HCD pointer and the corresponding FOTG210_HCD */ +static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd) +{ + return (struct fotg210_hcd *)(hcd->hcd_priv); +} +static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210) +{ + return container_of((void *) fotg210, struct usb_hcd, hcd_priv); +} + +/*-------------------------------------------------------------------------*/ + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct fotg210_caps { + /* these fields are specified as 8 and 16 bit registers, + * but some hosts can't perform 8 or 16 bit PCI accesses. + * some hosts treat caplength and hciversion as parts of a 32-bit + * register, others treat them as two separate registers, this + * affects the memory map for big endian controllers. + */ + u32 hc_capbase; +#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ + (fotg210_big_endian_capbase(fotg210) ? 24 : 0))) +#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ + (fotg210_big_endian_capbase(fotg210) ? 0 : 16))) + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ + u8 portroute[8]; /* nibbles for routing - offset 0xC */ +}; + + +/* Section 2.3 Host Controller Operational Registers */ +struct fotg210_regs { + + /* USBCMD: offset 0x00 */ + u32 command; + +/* EHCI 1.1 addendum */ +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved1; + /* PORTSC: offset 0x20 */ + u32 port_status; +/* 31:23 reserved */ +#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) + u32 reserved2[19]; + + /* OTGCSR: offet 0x70 */ + u32 otgcsr; +#define OTGCSR_HOST_SPD_TYP (3 << 22) +#define OTGCSR_A_BUS_DROP (1 << 5) +#define OTGCSR_A_BUS_REQ (1 << 4) + + /* OTGISR: offset 0x74 */ + u32 otgisr; +#define OTGISR_OVC (1 << 10) + + u32 reserved3[15]; + + /* GMIR: offset 0xB4 */ + u32 gmir; +#define GMIR_INT_POLARITY (1 << 3) /*Active High*/ +#define GMIR_MHC_INT (1 << 2) +#define GMIR_MOTG_INT (1 << 1) +#define GMIR_MDEV_INT (1 << 0) +}; + +/* Appendix C, Debug port ... intended for use with special "debug devices" + * that can help if there's no serial console. (nonstandard enumeration.) + */ +struct fotg210_dbg_port { + u32 control; +#define DBGP_OWNER (1<<30) +#define DBGP_ENABLED (1<<28) +#define DBGP_DONE (1<<16) +#define DBGP_INUSE (1<<10) +#define DBGP_ERRCODE(x) (((x)>>7)&0x07) +# define DBGP_ERR_BAD 1 +# define DBGP_ERR_SIGNAL 2 +#define DBGP_ERROR (1<<6) +#define DBGP_GO (1<<5) +#define DBGP_OUT (1<<4) +#define DBGP_LEN(x) (((x)>>0)&0x0f) + u32 pids; +#define DBGP_PID_GET(x) (((x)>>16)&0xff) +#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) + u32 data03; + u32 data47; + u32 address; +#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) +}; + +#ifdef CONFIG_EARLY_PRINTK_DBGP +#include +extern int __init early_dbgp_init(char *s); +extern struct console early_dbgp_console; +#endif /* CONFIG_EARLY_PRINTK_DBGP */ + +struct usb_hcd; + +static inline int xen_dbgp_reset_prep(struct usb_hcd *hcd) +{ + return 1; /* Shouldn't this be 0? */ +} + +static inline int xen_dbgp_external_startup(struct usb_hcd *hcd) +{ + return -1; +} + +#ifdef CONFIG_EARLY_PRINTK_DBGP +/* Call backs from fotg210 host driver to fotg210 debug driver */ +extern int dbgp_external_startup(struct usb_hcd *); +extern int dbgp_reset_prep(struct usb_hcd *hcd); +#else +static inline int dbgp_reset_prep(struct usb_hcd *hcd) +{ + return xen_dbgp_reset_prep(hcd); +} +static inline int dbgp_external_startup(struct usb_hcd *hcd) +{ + return xen_dbgp_external_startup(hcd); +} +#endif + +/*-------------------------------------------------------------------------*/ + +#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma) + +/* + * EHCI Specification 0.95 Section 3.5 + * QTD: describe data transfer components (buffer, direction, ...) + * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". + * + * These are associated only with "QH" (Queue Head) structures, + * used with control, bulk, and interrupt transfers. + */ +struct fotg210_qtd { + /* first part defined by EHCI spec */ + __hc32 hw_next; /* see EHCI 3.5.1 */ + __hc32 hw_alt_next; /* see EHCI 3.5.2 */ + __hc32 hw_token; /* see EHCI 3.5.3 */ +#define QTD_TOGGLE (1 << 31) /* data toggle */ +#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define QTD_IOC (1 << 15) /* interrupt on complete */ +#define QTD_CERR(tok) (((tok)>>10) & 0x3) +#define QTD_PID(tok) (((tok)>>8) & 0x3) +#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define QTD_STS_HALT (1 << 6) /* halted on error */ +#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define QTD_STS_STS (1 << 1) /* split transaction state */ +#define QTD_STS_PING (1 << 0) /* issue PING? */ + +#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE) +#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT) +#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS) + + __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ + __hc32 hw_buf_hi[5]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t qtd_dma; /* qtd address */ + struct list_head qtd_list; /* sw qtd list */ + struct urb *urb; /* qtd's urb */ + size_t length; /* length of buffer */ +} __aligned(32); + +/* mask NakCnt+T in qh->hw_alt_next */ +#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f) + +#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) + +/*-------------------------------------------------------------------------*/ + +/* type tag from {qh,itd,fstn}->hw_next */ +#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1)) + +/* + * Now the following defines are not converted using the + * cpu_to_le32() macro anymore, since we have to support + * "dynamic" switching between be and le support, so that the driver + * can be used on one system with SoC EHCI controller using big-endian + * descriptors as well as a normal little-endian PCI EHCI controller. + */ +/* values for that type tag */ +#define Q_TYPE_ITD (0 << 1) +#define Q_TYPE_QH (1 << 1) +#define Q_TYPE_SITD (2 << 1) +#define Q_TYPE_FSTN (3 << 1) + +/* next async queue entry, or pointer to interrupt/periodic QH */ +#define QH_NEXT(fotg210, dma) \ + (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH)) + +/* for periodic/async schedules and qtd lists, mark end of list */ +#define FOTG210_LIST_END(fotg210) \ + cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */ + +/* + * Entries in periodic shadow table are pointers to one of four kinds + * of data structure. That's dictated by the hardware; a type tag is + * encoded in the low bits of the hardware's periodic schedule. Use + * Q_NEXT_TYPE to get the tag. + * + * For entries in the async schedule, the type tag always says "qh". + */ +union fotg210_shadow { + struct fotg210_qh *qh; /* Q_TYPE_QH */ + struct fotg210_itd *itd; /* Q_TYPE_ITD */ + struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ + __hc32 *hw_next; /* (all types) */ + void *ptr; +}; + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.6 + * QH: describes control/bulk/interrupt endpoints + * See Fig 3-7 "Queue Head Structure Layout". + * + * These appear in both the async and (for interrupt) periodic schedules. + */ + +/* first part defined by EHCI spec */ +struct fotg210_qh_hw { + __hc32 hw_next; /* see EHCI 3.6.1 */ + __hc32 hw_info1; /* see EHCI 3.6.2 */ +#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ +#define QH_HEAD (1 << 15) /* Head of async reclamation list */ +#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ +#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ +#define QH_LOW_SPEED (1 << 12) +#define QH_FULL_SPEED (0 << 12) +#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ + __hc32 hw_info2; /* see EHCI 3.6.2 */ +#define QH_SMASK 0x000000ff +#define QH_CMASK 0x0000ff00 +#define QH_HUBADDR 0x007f0000 +#define QH_HUBPORT 0x3f800000 +#define QH_MULT 0xc0000000 + __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ + + /* qtd overlay (hardware parts of a struct fotg210_qtd) */ + __hc32 hw_qtd_next; + __hc32 hw_alt_next; + __hc32 hw_token; + __hc32 hw_buf[5]; + __hc32 hw_buf_hi[5]; +} __aligned(32); + +struct fotg210_qh { + struct fotg210_qh_hw *hw; /* Must come first */ + /* the rest is HCD-private */ + dma_addr_t qh_dma; /* address of qh */ + union fotg210_shadow qh_next; /* ptr to qh; or periodic */ + struct list_head qtd_list; /* sw qtd list */ + struct list_head intr_node; /* list of intr QHs */ + struct fotg210_qtd *dummy; + struct fotg210_qh *unlink_next; /* next on unlink list */ + + unsigned unlink_cycle; + + u8 needs_rescan; /* Dequeue during giveback */ + u8 qh_state; +#define QH_STATE_LINKED 1 /* HC sees this */ +#define QH_STATE_UNLINK 2 /* HC may still see this */ +#define QH_STATE_IDLE 3 /* HC doesn't see this */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ +#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ + + u8 xacterrs; /* XactErr retry counter */ +#define QH_XACTERR_MAX 32 /* XactErr retry limit */ + + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + u16 tt_usecs; /* tt downstream bandwidth */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + + struct usb_device *dev; /* access to TT */ + unsigned is_out:1; /* bulk or intr OUT */ + unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ +}; + +/*-------------------------------------------------------------------------*/ + +/* description of one iso transaction (up to 3 KB data if highspeed) */ +struct fotg210_iso_packet { + /* These will be copied to iTD when scheduling */ + u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ + __hc32 transaction; /* itd->hw_transaction[i] |= */ + u8 cross; /* buf crosses pages */ + /* for full speed OUT splits */ + u32 buf1; +}; + +/* temporary schedule data for packets from iso urbs (both speeds) + * each packet is one logical usb transaction to the device (not TT), + * beginning at stream->next_uframe + */ +struct fotg210_iso_sched { + struct list_head td_list; + unsigned span; + struct fotg210_iso_packet packet[0]; +}; + +/* + * fotg210_iso_stream - groups all (s)itds for this endpoint. + * acts like a qh would, if EHCI had them for ISO. + */ +struct fotg210_iso_stream { + /* first field matches fotg210_hq, but is NULL */ + struct fotg210_qh_hw *hw; + + u8 bEndpointAddress; + u8 highspeed; + struct list_head td_list; /* queued itds */ + struct list_head free_list; /* list of unused itds */ + struct usb_device *udev; + struct usb_host_endpoint *ep; + + /* output of (re)scheduling */ + int next_uframe; + __hc32 splits; + + /* the rest is derived from the endpoint descriptor, + * trusting urb->interval == f(epdesc->bInterval) and + * including the extra info for hw_bufp[0..2] + */ + u8 usecs, c_usecs; + u16 interval; + u16 tt_usecs; + u16 maxp; + u16 raw_mask; + unsigned bandwidth; + + /* This is used to initialize iTD's hw_bufp fields */ + __hc32 buf0; + __hc32 buf1; + __hc32 buf2; + + /* this is used to initialize sITD's tt info */ + __hc32 address; +}; + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.3 + * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" + * + * Schedule records for high speed iso xfers + */ +struct fotg210_itd { + /* first part defined by EHCI spec */ + __hc32 hw_next; /* see EHCI 3.3.1 */ + __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ +#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ +#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */ +#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */ +#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ +#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) +#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */ + +#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE) + + __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ + __hc32 hw_bufp_hi[7]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t itd_dma; /* for this itd */ + union fotg210_shadow itd_next; /* ptr to periodic q entry */ + + struct urb *urb; + struct fotg210_iso_stream *stream; /* endpoint's queue */ + struct list_head itd_list; /* list of stream's itds */ + + /* any/all hw_transactions here may be used by that urb */ + unsigned frame; /* where scheduled */ + unsigned pg; + unsigned index[8]; /* in urb->iso_frame_desc */ +} __aligned(32); + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.96 Section 3.7 + * Periodic Frame Span Traversal Node (FSTN) + * + * Manages split interrupt transactions (using TT) that span frame boundaries + * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN + * makes the HC jump (back) to a QH to scan for fs/ls QH completions until + * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. + */ +struct fotg210_fstn { + __hc32 hw_next; /* any periodic q entry */ + __hc32 hw_prev; /* qh or FOTG210_LIST_END */ + + /* the rest is HCD-private */ + dma_addr_t fstn_dma; + union fotg210_shadow fstn_next; /* ptr to periodic q entry */ +} __aligned(32); + +/*-------------------------------------------------------------------------*/ + +/* Prepare the PORTSC wakeup flags during controller suspend/resume */ + +#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ + fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup); + +#define fotg210_prepare_ports_for_controller_resume(fotg210) \ + fotg210_adjust_port_wakeup_flags(fotg210, false, false); + +/*-------------------------------------------------------------------------*/ + +/* + * Some EHCI controllers have a Transaction Translator built into the + * root hub. This is a non-standard feature. Each controller will need + * to add code to the following inline functions, and call them as + * needed (mostly in root hub code). + */ + +static inline unsigned int +fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc) +{ + return (readl(&fotg210->regs->otgcsr) + & OTGCSR_HOST_SPD_TYP) >> 22; +} + +/* Returns the speed of a device attached to a port on the root hub. */ +static inline unsigned int +fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc) +{ + switch (fotg210_get_speed(fotg210, portsc)) { + case 0: + return 0; + case 1: + return USB_PORT_STAT_LOW_SPEED; + case 2: + default: + return USB_PORT_STAT_HIGH_SPEED; + } +} + +/*-------------------------------------------------------------------------*/ + +#define fotg210_has_fsl_portno_bug(e) (0) + +/* + * While most USB host controllers implement their registers in + * little-endian format, a minority (celleb companion chip) implement + * them in big endian format. + * + * This attempts to support either format at compile time without a + * runtime penalty, or both formats with the additional overhead + * of checking a flag bit. + * + */ + +#define fotg210_big_endian_mmio(e) 0 +#define fotg210_big_endian_capbase(e) 0 + +static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210, + __u32 __iomem *regs) +{ + return readl(regs); +} + +static inline void fotg210_writel(const struct fotg210_hcd *fotg210, + const unsigned int val, __u32 __iomem *regs) +{ + writel(val, regs); +} + +/* cpu to fotg210 */ +static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x) +{ + return cpu_to_le32(x); +} + +/* fotg210 to cpu */ +static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x) +{ + return le32_to_cpu(x); +} + +static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210, + const __hc32 *x) +{ + return le32_to_cpup(x); +} + +/*-------------------------------------------------------------------------*/ + +static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) +{ + return fotg210_readl(fotg210, &fotg210->regs->frame_index); +} + +#define fotg210_itdlen(urb, desc, t) ({ \ + usb_pipein((urb)->pipe) ? \ + (desc)->length - FOTG210_ITD_LENGTH(t) : \ + FOTG210_ITD_LENGTH(t); \ +}) +/*-------------------------------------------------------------------------*/ + +#ifndef DEBUG +#define STUB_DEBUG_FILES +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------*/ + +#endif /* __LINUX_FOTG210_H */ -- cgit v1.2.3-58-ga151 From e01ee9f509a927158f670408b41127d4166db1c7 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:00:51 +0900 Subject: usb: gadget: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/at91_udc.c | 4 ++-- drivers/usb/gadget/atmel_usba_udc.c | 2 +- drivers/usb/gadget/bcm63xx_udc.c | 2 +- drivers/usb/gadget/fsl_mxc_udc.c | 4 ++-- drivers/usb/gadget/fsl_udc_core.c | 6 +++--- drivers/usb/gadget/hid.c | 2 +- drivers/usb/gadget/m66592-udc.c | 4 ++-- drivers/usb/gadget/mv_u3d_core.c | 12 ++++++------ drivers/usb/gadget/mv_udc_core.c | 4 ++-- drivers/usb/gadget/omap_udc.c | 2 +- drivers/usb/gadget/pxa25x_udc.c | 2 +- drivers/usb/gadget/pxa27x_udc.c | 2 +- drivers/usb/gadget/r8a66597-udc.c | 2 +- drivers/usb/gadget/s3c-hsotg.c | 4 ++-- drivers/usb/gadget/s3c-hsudc.c | 4 ++-- drivers/usb/gadget/s3c2410_udc.c | 2 +- 16 files changed, 29 insertions(+), 29 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index d9a6add0c852..d237429dd93a 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1697,7 +1697,7 @@ static int at91udc_probe(struct platform_device *pdev) int retval; struct resource *res; - if (!dev->platform_data && !pdev->dev.of_node) { + if (!dev_get_platdata(dev) && !pdev->dev.of_node) { /* small (so we copy it) but critical! */ DBG("missing platform_data\n"); return -ENODEV; @@ -1728,7 +1728,7 @@ static int at91udc_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) at91udc_of_init(udc, pdev->dev.of_node); else - memcpy(&udc->board, dev->platform_data, + memcpy(&udc->board, dev_get_platdata(dev), sizeof(struct at91_udc_data)); udc->pdev = pdev; udc->enabled = 0; diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index f018017e3a77..40d23384b716 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1931,7 +1931,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, static struct usba_ep * usba_udc_pdata(struct platform_device *pdev, struct usba_udc *udc) { - struct usba_platform_data *pdata = pdev->dev.platform_data; + struct usba_platform_data *pdata = dev_get_platdata(&pdev->dev); struct usba_ep *eps; int i; diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index fd24cb4540a4..c58fcf1ebe41 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -2313,7 +2313,7 @@ static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc) static int bcm63xx_udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct bcm63xx_usbd_platform_data *pd = dev->platform_data; + struct bcm63xx_usbd_platform_data *pd = dev_get_platdata(dev); struct bcm63xx_udc *udc; struct resource *res; int rc = -ENOMEM, i, irq; diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/fsl_mxc_udc.c index d3bd7b095ba3..9b140fc4d3bc 100644 --- a/drivers/usb/gadget/fsl_mxc_udc.c +++ b/drivers/usb/gadget/fsl_mxc_udc.c @@ -33,7 +33,7 @@ int fsl_udc_clk_init(struct platform_device *pdev) unsigned long freq; int ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); mxc_ipg_clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(mxc_ipg_clk)) { @@ -80,7 +80,7 @@ eclkrate: int fsl_udc_clk_finalize(struct platform_device *pdev) { - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret = 0; /* workaround ENGcm09152 for i.MX35 */ diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index a766a4ca1cb7..36ac7cfba91d 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2248,7 +2248,7 @@ static int __init struct_udc_setup(struct fsl_udc *udc, struct fsl_usb2_platform_data *pdata; size_t size; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); udc->phy_mode = pdata->phy_mode; udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL); @@ -2343,7 +2343,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) return -ENOMEM; } - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); udc_controller->pdata = pdata; spin_lock_init(&udc_controller->lock); udc_controller->stopped = 1; @@ -2524,7 +2524,7 @@ err_kfree: static int __exit fsl_udc_remove(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); DECLARE_COMPLETION(done); diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index c36260ea8bf2..778613eb37af 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -185,7 +185,7 @@ static int __exit hid_unbind(struct usb_composite_dev *cdev) static int __init hidg_plat_driver_probe(struct platform_device *pdev) { - struct hidg_func_descriptor *func = pdev->dev.platform_data; + struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev); struct hidg_func_node *entry; if (!func) { diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 46ba9838c3a0..d5f050d30edf 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1584,7 +1584,7 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } - if (pdev->dev.platform_data == NULL) { + if (dev_get_platdata(&pdev->dev) == NULL) { dev_err(&pdev->dev, "no platform data\n"); ret = -ENODEV; goto clean_up; @@ -1598,7 +1598,7 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } - m66592->pdata = pdev->dev.platform_data; + m66592->pdata = dev_get_platdata(&pdev->dev); m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK; spin_lock_init(&m66592->lock); diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index ec6a2d290398..bbb6e98c4384 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1109,7 +1109,7 @@ static int mv_u3d_controller_reset(struct mv_u3d *u3d) static int mv_u3d_enable(struct mv_u3d *u3d) { - struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); int retval; if (u3d->active) @@ -1138,7 +1138,7 @@ static int mv_u3d_enable(struct mv_u3d *u3d) static void mv_u3d_disable(struct mv_u3d *u3d) { - struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); if (u3d->clock_gating && u3d->active) { dev_dbg(u3d->dev, "disable u3d\n"); if (pdata->phy_deinit) @@ -1246,7 +1246,7 @@ static int mv_u3d_start(struct usb_gadget *g, struct usb_gadget_driver *driver) { struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); - struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); unsigned long flags; if (u3d->driver) @@ -1277,7 +1277,7 @@ static int mv_u3d_stop(struct usb_gadget *g, struct usb_gadget_driver *driver) { struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); - struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); unsigned long flags; u3d->vbus_valid_detect = 0; @@ -1794,12 +1794,12 @@ static int mv_u3d_remove(struct platform_device *dev) static int mv_u3d_probe(struct platform_device *dev) { struct mv_u3d *u3d = NULL; - struct mv_usb_platform_data *pdata = dev->dev.platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev); int retval = 0; struct resource *r; size_t size; - if (!dev->dev.platform_data) { + if (!dev_get_platdata(&dev->dev)) { dev_err(&dev->dev, "missing platform_data\n"); retval = -ENODEV; goto err_pdata; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index c2a57023e467..104cdbea635a 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2100,7 +2100,7 @@ static int mv_udc_remove(struct platform_device *pdev) static int mv_udc_probe(struct platform_device *pdev) { - struct mv_usb_platform_data *pdata = pdev->dev.platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mv_udc *udc; int retval = 0; struct resource *r; @@ -2118,7 +2118,7 @@ static int mv_udc_probe(struct platform_device *pdev) } udc->done = &release_done; - udc->pdata = pdev->dev.platform_data; + udc->pdata = dev_get_platdata(&pdev->dev); spin_lock_init(&udc->lock); udc->dev = pdev; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index b8ed74a823cb..83957cc225d9 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2734,7 +2734,7 @@ static int omap_udc_probe(struct platform_device *pdev) int hmc; struct usb_phy *xceiv = NULL; const char *type = NULL; - struct omap_usb_config *config = pdev->dev.platform_data; + struct omap_usb_config *config = dev_get_platdata(&pdev->dev); struct clk *dc_clk = NULL; struct clk *hhc_clk = NULL; diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 95c531d5aa4f..cc9207473dbc 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2117,7 +2117,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) /* other non-static parts of init */ dev->dev = &pdev->dev; - dev->mach = pdev->dev.platform_data; + dev->mach = dev_get_platdata(&pdev->dev); dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 41cea9566ac8..3c97da7760da 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2422,7 +2422,7 @@ static int pxa_udc_probe(struct platform_device *pdev) return udc->irq; udc->dev = &pdev->dev; - udc->mach = pdev->dev.platform_data; + udc->mach = dev_get_platdata(&pdev->dev); udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); gpio = udc->mach->gpio_pullup; diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index c6af649f3240..68be48d33404 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1910,7 +1910,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) spin_lock_init(&r8a66597->lock); platform_set_drvdata(pdev, r8a66597); - r8a66597->pdata = pdev->dev.platform_data; + r8a66597->pdata = dev_get_platdata(&pdev->dev); r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; r8a66597->gadget.ops = &r8a66597_gadget_ops; diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 616ed51f8585..d69b36a99dbc 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3451,7 +3451,7 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg) static int s3c_hsotg_probe(struct platform_device *pdev) { - struct s3c_hsotg_plat *plat = pdev->dev.platform_data; + struct s3c_hsotg_plat *plat = dev_get_platdata(&pdev->dev); struct usb_phy *phy; struct device *dev = &pdev->dev; struct s3c_hsotg_ep *eps; @@ -3470,7 +3470,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev) phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); if (IS_ERR(phy)) { /* Fallback for pdata */ - plat = pdev->dev.platform_data; + plat = dev_get_platdata(&pdev->dev); if (!plat) { dev_err(&pdev->dev, "no platform data or transceiver defined\n"); return -EPROBE_DEFER; diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index b1f0771fbd3d..1a1a41498db2 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1262,7 +1262,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; struct s3c_hsudc *hsudc; - struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data; + struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev); int ret, i; hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) + @@ -1275,7 +1275,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); hsudc->dev = dev; - hsudc->pd = pdev->dev.platform_data; + hsudc->pd = dev_get_platdata(&pdev->dev); hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 09c4f70c93c4..c72d810e6b36 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1809,7 +1809,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) } spin_lock_init(&udc->lock); - udc_info = pdev->dev.platform_data; + udc_info = dev_get_platdata(&pdev->dev); rsrc_start = S3C2410_PA_USBDEV; rsrc_len = S3C24XX_SZ_USBDEV; -- cgit v1.2.3-58-ga151 From 19f9e188deb4bbcd5fc588f39dc63bdfa9103827 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:02:13 +0900 Subject: usb: phy: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsl-usb.c | 6 +++--- drivers/usb/phy/phy-gpio-vbus-usb.c | 10 +++++----- drivers/usb/phy/phy-msm-usb.c | 4 ++-- drivers/usb/phy/phy-mv-u3d-usb.c | 2 +- drivers/usb/phy/phy-mv-usb.c | 2 +- drivers/usb/phy/phy-nop.c | 3 ++- drivers/usb/phy/phy-omap-control.c | 3 ++- drivers/usb/phy/phy-rcar-usb.c | 4 ++-- drivers/usb/phy/phy-samsung-usb2.c | 2 +- drivers/usb/phy/phy-samsung-usb3.c | 2 +- drivers/usb/phy/phy-twl4030-usb.c | 2 +- drivers/usb/phy/phy-twl6030-usb.c | 2 +- 12 files changed, 22 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index e771bafb9f1d..e965cf65d696 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -834,7 +834,7 @@ int usb_otg_start(struct platform_device *pdev) int status; struct resource *res; u32 temp; - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); p_otg = container_of(otg_trans, struct fsl_otg, phy); fsm = &p_otg->fsm; @@ -1105,7 +1105,7 @@ static int fsl_otg_probe(struct platform_device *pdev) { int ret; - if (!pdev->dev.platform_data) + if (!dev_get_platdata(&pdev->dev)) return -ENODEV; /* configure the OTG */ @@ -1137,7 +1137,7 @@ static int fsl_otg_probe(struct platform_device *pdev) static int fsl_otg_remove(struct platform_device *pdev) { - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); usb_remove_phy(&fsl_otg_dev->phy); free_irq(fsl_otg_dev->irq, fsl_otg_dev); diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 8443335c2ea0..b2f29c9aebbf 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -101,7 +101,7 @@ static void gpio_vbus_work(struct work_struct *work) { struct gpio_vbus_data *gpio_vbus = container_of(work, struct gpio_vbus_data, work.work); - struct gpio_vbus_mach_info *pdata = gpio_vbus->dev->platform_data; + struct gpio_vbus_mach_info *pdata = dev_get_platdata(gpio_vbus->dev); int gpio, status, vbus; if (!gpio_vbus->phy.otg->gadget) @@ -155,7 +155,7 @@ static void gpio_vbus_work(struct work_struct *work) static irqreturn_t gpio_vbus_irq(int irq, void *data) { struct platform_device *pdev = data; - struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev); struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); struct usb_otg *otg = gpio_vbus->phy.otg; @@ -182,7 +182,7 @@ static int gpio_vbus_set_peripheral(struct usb_otg *otg, gpio_vbus = container_of(otg->phy, struct gpio_vbus_data, phy); pdev = to_platform_device(gpio_vbus->dev); - pdata = gpio_vbus->dev->platform_data; + pdata = dev_get_platdata(gpio_vbus->dev); gpio = pdata->gpio_pullup; if (!gadget) { @@ -243,7 +243,7 @@ static int gpio_vbus_set_suspend(struct usb_phy *phy, int suspend) static int __init gpio_vbus_probe(struct platform_device *pdev) { - struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev); struct gpio_vbus_data *gpio_vbus; struct resource *res; int err, gpio, irq; @@ -352,7 +352,7 @@ err_gpio: static int __exit gpio_vbus_remove(struct platform_device *pdev) { struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); - struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev); int gpio = pdata->gpio_vbus; device_init_wakeup(&pdev->dev, 0); diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index d08f33435e96..e9d4cd960ecd 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1419,7 +1419,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) struct usb_phy *phy; dev_info(&pdev->dev, "msm_otg probe\n"); - if (!pdev->dev.platform_data) { + if (!dev_get_platdata(&pdev->dev)) { dev_err(&pdev->dev, "No platform data given. Bailing out\n"); return -ENODEV; } @@ -1436,7 +1436,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) return -ENOMEM; } - motg->pdata = pdev->dev.platform_data; + motg->pdata = dev_get_platdata(&pdev->dev); phy = &motg->phy; phy->dev = &pdev->dev; diff --git a/drivers/usb/phy/phy-mv-u3d-usb.c b/drivers/usb/phy/phy-mv-u3d-usb.c index 1568ea63e338..6688023021d3 100644 --- a/drivers/usb/phy/phy-mv-u3d-usb.c +++ b/drivers/usb/phy/phy-mv-u3d-usb.c @@ -271,7 +271,7 @@ static int mv_u3d_phy_probe(struct platform_device *pdev) void __iomem *phy_base; int ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); return -EINVAL; diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index 4a6b03c73876..03d16ff24c02 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -673,7 +673,7 @@ int mv_otg_remove(struct platform_device *pdev) static int mv_otg_probe(struct platform_device *pdev) { - struct mv_usb_platform_data *pdata = pdev->dev.platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mv_otg *mvotg; struct usb_otg *otg; struct resource *r; diff --git a/drivers/usb/phy/phy-nop.c b/drivers/usb/phy/phy-nop.c index 55445e5d72e5..f52b7f89eef1 100644 --- a/drivers/usb/phy/phy-nop.c +++ b/drivers/usb/phy/phy-nop.c @@ -142,7 +142,8 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) static int nop_usb_xceiv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; + struct nop_usb_xceiv_platform_data *pdata = + dev_get_platdata(&pdev->dev); struct nop_usb_xceiv *nop; enum usb_phy_type type = USB_PHY_TYPE_USB2; int err; diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c index 1419ceda9759..a4dda8e12562 100644 --- a/drivers/usb/phy/phy-omap-control.c +++ b/drivers/usb/phy/phy-omap-control.c @@ -197,7 +197,8 @@ static int omap_control_usb_probe(struct platform_device *pdev) { struct resource *res; struct device_node *np = pdev->dev.of_node; - struct omap_control_usb_platform_data *pdata = pdev->dev.platform_data; + struct omap_control_usb_platform_data *pdata = + dev_get_platdata(&pdev->dev); control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb), GFP_KERNEL); diff --git a/drivers/usb/phy/phy-rcar-usb.c b/drivers/usb/phy/phy-rcar-usb.c index deb7f97f37c7..33265a5b2cdf 100644 --- a/drivers/usb/phy/phy-rcar-usb.c +++ b/drivers/usb/phy/phy-rcar-usb.c @@ -83,7 +83,7 @@ static int rcar_usb_phy_init(struct usb_phy *phy) { struct rcar_usb_phy_priv *priv = usb_phy_to_priv(phy); struct device *dev = phy->dev; - struct rcar_phy_platform_data *pdata = dev->platform_data; + struct rcar_phy_platform_data *pdata = dev_get_platdata(dev); void __iomem *reg0 = priv->reg0; void __iomem *reg1 = priv->reg1; static const u8 ovcn_act[] = { OVC0_ACT, OVC1_ACT, OVC2_ACT }; @@ -184,7 +184,7 @@ static int rcar_usb_phy_probe(struct platform_device *pdev) void __iomem *reg0, *reg1 = NULL; int ret; - if (!pdev->dev.platform_data) { + if (!dev_get_platdata(&pdev->dev)) { dev_err(dev, "No platform data\n"); return -EINVAL; } diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c index 758b86d0fcb3..ff70e4b19b97 100644 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -359,7 +359,7 @@ static int samsung_usb2phy_probe(struct platform_device *pdev) { struct samsung_usbphy *sphy; struct usb_otg *otg; - struct samsung_usbphy_data *pdata = pdev->dev.platform_data; + struct samsung_usbphy_data *pdata = dev_get_platdata(&pdev->dev); const struct samsung_usbphy_drvdata *drv_data; struct device *dev = &pdev->dev; struct resource *phy_mem; diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c index 300e0cf5e31f..c6eb22213de6 100644 --- a/drivers/usb/phy/phy-samsung-usb3.c +++ b/drivers/usb/phy/phy-samsung-usb3.c @@ -231,7 +231,7 @@ static void samsung_usb3phy_shutdown(struct usb_phy *phy) static int samsung_usb3phy_probe(struct platform_device *pdev) { struct samsung_usbphy *sphy; - struct samsung_usbphy_data *pdata = pdev->dev.platform_data; + struct samsung_usbphy_data *pdata = dev_get_platdata(&pdev->dev); struct device *dev = &pdev->dev; struct resource *phy_mem; void __iomem *phy_base; diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c index 8f78d2d40722..90730c8762b8 100644 --- a/drivers/usb/phy/phy-twl4030-usb.c +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -648,7 +648,7 @@ static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) static int twl4030_usb_probe(struct platform_device *pdev) { - struct twl4030_usb_data *pdata = pdev->dev.platform_data; + struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev); struct twl4030_usb *twl; int status, err; struct usb_otg *otg; diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 1753bd367e0a..16dbc9382678 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -324,7 +324,7 @@ static int twl6030_usb_probe(struct platform_device *pdev) int status, err; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - struct twl4030_usb_data *pdata = dev->platform_data; + struct twl4030_usb_data *pdata = dev_get_platdata(dev); twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL); if (!twl) -- cgit v1.2.3-58-ga151 From c1a7d67c1901347bdb3d06536cd69d018fbf2c4b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:03:12 +0900 Subject: usb: musb: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/musb/am35x.c | 14 +++++++------- drivers/usb/musb/blackfin.c | 2 +- drivers/usb/musb/da8xx.c | 2 +- drivers/usb/musb/davinci.c | 2 +- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/omap2430.c | 8 ++++---- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/musb/ux500.c | 2 +- drivers/usb/musb/ux500_dma.c | 2 +- 10 files changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 2231850c0625..baebc39a571b 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -218,7 +218,7 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci) struct musb *musb = hci; void __iomem *reg_base = musb->ctrl_base; struct device *dev = musb->controller; - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct omap_musb_board_data *data = plat->board_data; struct usb_otg *otg = musb->xceiv->otg; unsigned long flags; @@ -335,7 +335,7 @@ eoi: static int am35x_musb_set_mode(struct musb *musb, u8 musb_mode) { struct device *dev = musb->controller; - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct omap_musb_board_data *data = plat->board_data; int retval = 0; @@ -350,7 +350,7 @@ static int am35x_musb_set_mode(struct musb *musb, u8 musb_mode) static int am35x_musb_init(struct musb *musb) { struct device *dev = musb->controller; - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct omap_musb_board_data *data = plat->board_data; void __iomem *reg_base = musb->ctrl_base; u32 rev; @@ -394,7 +394,7 @@ static int am35x_musb_init(struct musb *musb) static int am35x_musb_exit(struct musb *musb) { struct device *dev = musb->controller; - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct omap_musb_board_data *data = plat->board_data; del_timer_sync(&otg_workaround); @@ -456,7 +456,7 @@ static u64 am35x_dmamask = DMA_BIT_MASK(32); static int am35x_probe(struct platform_device *pdev) { - struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); struct platform_device *musb; struct am35x_glue *glue; @@ -577,7 +577,7 @@ static int am35x_remove(struct platform_device *pdev) static int am35x_suspend(struct device *dev) { struct am35x_glue *glue = dev_get_drvdata(dev); - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct omap_musb_board_data *data = plat->board_data; /* Shutdown the on-chip PHY and its PLL. */ @@ -593,7 +593,7 @@ static int am35x_suspend(struct device *dev) static int am35x_resume(struct device *dev) { struct am35x_glue *glue = dev_get_drvdata(dev); - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct omap_musb_board_data *data = plat->board_data; int ret; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 6ba8439bd5a6..3e63379ec469 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -451,7 +451,7 @@ static u64 bfin_dmamask = DMA_BIT_MASK(32); static int bfin_probe(struct platform_device *pdev) { struct resource musb_resources[2]; - struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); struct platform_device *musb; struct bfin_glue *glue; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 0da6f648a9fe..7a46521c0800 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -477,7 +477,7 @@ static u64 da8xx_dmamask = DMA_BIT_MASK(32); static int da8xx_probe(struct platform_device *pdev) { struct resource musb_resources[2]; - struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); struct platform_device *musb; struct da8xx_glue *glue; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index f8aeaf2e2cd1..12ab66d26d8f 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -510,7 +510,7 @@ static u64 davinci_dmamask = DMA_BIT_MASK(32); static int davinci_probe(struct platform_device *pdev) { struct resource musb_resources[2]; - struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); struct platform_device *musb; struct davinci_glue *glue; struct clk *clk; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 80ffd7ee55df..b9d741614f8c 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1783,7 +1783,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) { int status; struct musb *musb; - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); /* The driver might handle more features than the board; OK. * Fail when the board needs a feature that's not enabled. diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 23f511f7df6d..4816b2f972d0 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -478,7 +478,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, u8 id) { struct device *dev = glue->dev; struct platform_device *pdev = to_platform_device(dev); - struct musb_hdrc_platform_data *pdata = dev->platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); struct device_node *np = pdev->dev.of_node; struct musb_hdrc_config *config; struct platform_device *musb; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 6708a3b78ad8..ebb46eca1791 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -255,7 +255,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) { struct musb *musb = glue_to_musb(glue); struct device *dev = musb->controller; - struct musb_hdrc_platform_data *pdata = dev->platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); struct omap_musb_board_data *data = pdata->board_data; struct usb_otg *otg = musb->xceiv->otg; @@ -341,7 +341,7 @@ static int omap2430_musb_init(struct musb *musb) int status = 0; struct device *dev = musb->controller; struct omap2430_glue *glue = dev_get_drvdata(dev->parent); - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct omap_musb_board_data *data = plat->board_data; /* We require some kind of external transceiver, hooked @@ -412,7 +412,7 @@ static void omap2430_musb_enable(struct musb *musb) unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct device *dev = musb->controller; struct omap2430_glue *glue = dev_get_drvdata(dev->parent); - struct musb_hdrc_platform_data *pdata = dev->platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); struct omap_musb_board_data *data = pdata->board_data; switch (glue->status) { @@ -482,7 +482,7 @@ static u64 omap2430_dmamask = DMA_BIT_MASK(32); static int omap2430_probe(struct platform_device *pdev) { struct resource musb_resources[2]; - struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); struct omap_musb_board_data *data; struct platform_device *musb; struct omap2430_glue *glue; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 2c06a8969a9f..2196ee6e79fe 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1157,7 +1157,7 @@ static u64 tusb_dmamask = DMA_BIT_MASK(32); static int tusb_probe(struct platform_device *pdev) { struct resource musb_resources[2]; - struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); struct platform_device *musb; struct tusb6010_glue *glue; diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index fce71b605936..59256b12f746 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -227,7 +227,7 @@ ux500_of_probe(struct platform_device *pdev, struct device_node *np) static int ux500_probe(struct platform_device *pdev) { struct resource musb_resources[2]; - struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); struct device_node *np = pdev->dev.of_node; struct platform_device *musb; struct ux500_glue *glue; diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 1ce214e5b7ad..e51dd9b88e71 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -286,7 +286,7 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller) struct ux500_dma_channel *ux500_channel = NULL; struct musb *musb = controller->private_data; struct device *dev = musb->controller; - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct ux500_musb_board_data *data; struct dma_channel *dma_channel = NULL; char **chan_names; -- cgit v1.2.3-58-ga151 From f074245960e8fec9948eb8022322e43670ace4e5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:06:20 +0900 Subject: usb: renesas: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index cfd205036aba..3b39757c13bc 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -416,7 +416,7 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) */ static int usbhs_probe(struct platform_device *pdev) { - struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; + struct renesas_usbhs_platform_info *info = dev_get_platdata(&pdev->dev); struct renesas_usbhs_driver_callback *dfunc; struct usbhs_priv *priv; struct resource *res, *irq_res; @@ -558,7 +558,7 @@ probe_end_pipe_exit: static int usbhs_remove(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); - struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; + struct renesas_usbhs_platform_info *info = dev_get_platdata(&pdev->dev); struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback; dev_dbg(&pdev->dev, "usb remove\n"); -- cgit v1.2.3-58-ga151 From 941ea3616c747545d0278fc432fb7919b6d0d8f0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 31 Jul 2013 09:21:25 +0300 Subject: usb: dwc3: use dev_get_platdata() Use the wrapper function for retrieving the platform_data instead of accessing dev->platform_data directly. While at that also make change 'node' initialization to use the dev pointer. Inspired-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index da0a4b8b33ed..3ff6f0ad01df 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -350,11 +350,11 @@ static void dwc3_core_exit(struct dwc3 *dwc) static int dwc3_probe(struct platform_device *pdev) { - struct dwc3_platform_data *pdata = pdev->dev.platform_data; - struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct dwc3_platform_data *pdata = dev_get_platdata(dev); + struct device_node *node = dev->of_node; struct resource *res; struct dwc3 *dwc; - struct device *dev = &pdev->dev; int ret = -ENOMEM; -- cgit v1.2.3-58-ga151 From f3e94aa15dc3d9155f8fc4a3295866d7a207b4e5 Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Tue, 30 Jul 2013 19:51:20 -0700 Subject: usb: core: don't try to reset_device() a port that got just disconnected The USB hub driver's event handler contains a check to catch SuperSpeed devices that transitioned into the SS.Inactive state and tries to fix them with a reset. It decides whether to do a plain hub port reset or call the usb_reset_device() function based on whether there was a device attached to the port. However, there are device/hub combinations (found with a JetFlash Transcend mass storage stick (8564:1000) on the root hub of an Intel LynxPoint PCH) which can transition to the SS.Inactive state on disconnect (and stay there long enough for the host to notice). In this case, above-mentioned reset check will call usb_reset_device() on the stale device data structure. The kernel will send pointless LPM control messages to the no longer connected device address and can even cause several 5 second khubd stalls on some (buggy?) host controllers, before finally accepting the device's fate amongst a flurry of error messages. This patch makes the choice of reset dependent on the port status that has just been read from the hub in addition to the existence of an in-kernel data structure for the device, and only proceeds with the more extensive reset if both are valid. Signed-off-by: Julius Werner Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 91a0e84a68cd..c881830af55f 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4796,7 +4796,8 @@ static void hub_events(void) hub->ports[i - 1]->child; dev_dbg(hub_dev, "warm reset port %d\n", i); - if (!udev) { + if (!udev || !(portstatus & + USB_PORT_STAT_CONNECTION)) { status = hub_port_reset(hub, i, NULL, HUB_BH_RESET_TIME, true); @@ -4806,8 +4807,8 @@ static void hub_events(void) usb_lock_device(udev); status = usb_reset_device(udev); usb_unlock_device(udev); + connect_change = 0; } - connect_change = 0; } if (connect_change) -- cgit v1.2.3-58-ga151 From 720ce6e4b1ba83aa1ed526a47d36852ac6a25d7e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:25:19 +0900 Subject: USB: c67x00: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/c67x00/c67x00-drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c index fe815ecd557e..8db3380c3329 100644 --- a/drivers/usb/c67x00/c67x00-drv.c +++ b/drivers/usb/c67x00/c67x00-drv.c @@ -131,7 +131,7 @@ static int c67x00_drv_probe(struct platform_device *pdev) if (!res2) return -ENODEV; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) return -ENODEV; @@ -154,7 +154,7 @@ static int c67x00_drv_probe(struct platform_device *pdev) spin_lock_init(&c67x00->hpi.lock); c67x00->hpi.regstep = pdata->hpi_regstep; - c67x00->pdata = pdev->dev.platform_data; + c67x00->pdata = dev_get_platdata(&pdev->dev); c67x00->pdev = pdev; c67x00_ll_init(c67x00); -- cgit v1.2.3-58-ga151 From ace0a5f9471ef51349dba59ff57beee2c3748a34 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:06:20 +0900 Subject: usb: renesas: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index cfd205036aba..3b39757c13bc 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -416,7 +416,7 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) */ static int usbhs_probe(struct platform_device *pdev) { - struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; + struct renesas_usbhs_platform_info *info = dev_get_platdata(&pdev->dev); struct renesas_usbhs_driver_callback *dfunc; struct usbhs_priv *priv; struct resource *res, *irq_res; @@ -558,7 +558,7 @@ probe_end_pipe_exit: static int usbhs_remove(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); - struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; + struct renesas_usbhs_platform_info *info = dev_get_platdata(&pdev->dev); struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback; dev_dbg(&pdev->dev, "usb remove\n"); -- cgit v1.2.3-58-ga151 From b977a3068a284b2ad4612cdb8ca326cbd2a7ffc9 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:05:28 +0900 Subject: usb: misc: usb3503: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index c3578393ddef..cbb6b7841653 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -179,7 +179,7 @@ err_hubmode: static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct usb3503_platform_data *pdata = i2c->dev.platform_data; + struct usb3503_platform_data *pdata = dev_get_platdata(&i2c->dev); struct device_node *np = i2c->dev.of_node; struct usb3503 *hub; int err = -ENOMEM; -- cgit v1.2.3-58-ga151 From 15b7336e02d998720c5ace47036f7e539365bb05 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 30 Jul 2013 15:35:40 -0400 Subject: USB: simplify the interface of usb_get_status() This patch simplifies the interface presented by usb_get_status(). Instead of forcing callers to check for the proper data length and convert the status value to host byte order, the function will now do these things itself. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 10 ++-------- drivers/usb/core/message.c | 13 +++++++++---- drivers/usb/misc/usbtest.c | 9 ++++----- 3 files changed, 15 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c881830af55f..11a92198bfc7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1464,11 +1464,10 @@ static int hub_configure(struct usb_hub *hub, * and battery-powered root hubs (may provide just 8 mA). */ ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus); - if (ret < 2) { + if (ret) { message = "can't get hub status"; goto fail; } - le16_to_cpus(&hubstatus); hcd = bus_to_hcd(hdev->bus); if (hdev == hdev->bus->root_hub) { if (hcd->power_budget > 0) @@ -3101,8 +3100,6 @@ static int finish_port_resume(struct usb_device *udev) if (status == 0) { devstatus = 0; status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); - if (status >= 0) - status = (status > 0 ? 0 : -ENODEV); /* If a normal resume failed, try doing a reset-resume */ if (status && !udev->reset_resume && udev->persist_enabled) { @@ -3123,7 +3120,6 @@ static int finish_port_resume(struct usb_device *udev) */ } else if (udev->actconfig && !udev->reset_resume) { if (!hub_is_superspeed(udev->parent)) { - le16_to_cpus(&devstatus); if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), @@ -3135,7 +3131,6 @@ static int finish_port_resume(struct usb_device *udev) } else { status = usb_get_status(udev, USB_RECIP_INTERFACE, 0, &devstatus); - le16_to_cpus(&devstatus); if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW)) status = @@ -4481,11 +4476,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstat); - if (status < 2) { + if (status) { dev_dbg(&udev->dev, "get status %d ?\n", status); goto loop_disable; } - le16_to_cpus(&devstat); if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_err(&udev->dev, "can't connect bus-powered hub " diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index e7ee1e451660..6549a975b0c5 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -934,13 +934,13 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) * * This call is synchronous, and may not be used in an interrupt context. * - * Returns the number of bytes received on success, or else the status code - * returned by the underlying usb_control_msg() call. + * Returns 0 and the status value in *@data (in host byte order) on success, + * or else the status code from the underlying usb_control_msg() call. */ int usb_get_status(struct usb_device *dev, int type, int target, void *data) { int ret; - u16 *status = kmalloc(sizeof(*status), GFP_KERNEL); + __le16 *status = kmalloc(sizeof(*status), GFP_KERNEL); if (!status) return -ENOMEM; @@ -949,7 +949,12 @@ int usb_get_status(struct usb_device *dev, int type, int target, void *data) USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status, sizeof(*status), USB_CTRL_GET_TIMEOUT); - *(u16 *)data = *status; + if (ret == 2) { + *(u16 *) data = le16_to_cpu(*status); + ret = 0; + } else if (ret >= 0) { + ret = -EIO; + } kfree(status); return ret; } diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 8b4ca1cb450a..aa28ac8c7607 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -747,9 +747,9 @@ static int ch9_postconfig(struct usbtest_dev *dev) /* [9.4.5] get_status always works */ retval = usb_get_status(udev, USB_RECIP_DEVICE, 0, dev->buf); - if (retval != 2) { + if (retval) { dev_err(&iface->dev, "get dev status --> %d\n", retval); - return (retval < 0) ? retval : -EDOM; + return retval; } /* FIXME configuration.bmAttributes says if we could try to set/clear @@ -758,9 +758,9 @@ static int ch9_postconfig(struct usbtest_dev *dev) retval = usb_get_status(udev, USB_RECIP_INTERFACE, iface->altsetting[0].desc.bInterfaceNumber, dev->buf); - if (retval != 2) { + if (retval) { dev_err(&iface->dev, "get interface status --> %d\n", retval); - return (retval < 0) ? retval : -EDOM; + return retval; } /* FIXME get status for each endpoint in the interface */ @@ -1351,7 +1351,6 @@ static int verify_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) ep, retval); return retval; } - le16_to_cpus(&status); if (status != 1) { ERROR(tdev, "ep %02x bogus status: %04x != 1\n", ep, status); return -EINVAL; -- cgit v1.2.3-58-ga151 From 28e861658e23ca94692f98e245d254c75c8088a7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 30 Jul 2013 15:37:33 -0400 Subject: USB: refactor code for enabling/disabling remote wakeup The hub driver is inconsistent in its organization of code for enabling and disabling remote wakeup. There is a special routine to disable wakeup for SuperSpeed devices but not for slower devices, and there is no special routine to enable wakeup. This patch refactors the code. It renames and changes the existing function to make it handle both SuperSpeed and non-SuperSpeed devices, and it adds a corresponding routine to enable remote wakeup. It also changes the speed determination to look at the device's speed rather than the speed of the parent hub -- this shouldn't make any difference because a SuperSpeed device always has to be attached to a SuperSpeed hub and conversely. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 98 ++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 52 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 11a92198bfc7..c376c8bad40c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2838,18 +2838,50 @@ void usb_enable_ltm(struct usb_device *udev) EXPORT_SYMBOL_GPL(usb_enable_ltm); /* - * usb_disable_function_remotewakeup - disable usb3.0 - * device's function remote wakeup + * usb_enable_remote_wakeup - enable remote wakeup for a device * @udev: target device * - * Assume there's only one function on the USB 3.0 - * device and disable remote wake for the first - * interface. FIXME if the interface association - * descriptor shows there's more than one function. + * For USB-2 devices: Set the device's remote wakeup feature. + * + * For USB-3 devices: Assume there's only one function on the device and + * enable remote wake for the first interface. FIXME if the interface + * association descriptor shows there's more than one function. */ -static int usb_disable_function_remotewakeup(struct usb_device *udev) +static int usb_enable_remote_wakeup(struct usb_device *udev) { - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + if (udev->speed < USB_SPEED_SUPER) + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + else + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, + USB_INTRF_FUNC_SUSPEND_RW | + USB_INTRF_FUNC_SUSPEND_LP, + NULL, 0, USB_CTRL_SET_TIMEOUT); +} + +/* + * usb_disable_remote_wakeup - disable remote wakeup for a device + * @udev: target device + * + * For USB-2 devices: Clear the device's remote wakeup feature. + * + * For USB-3 devices: Assume there's only one function on the device and + * disable remote wake for the first interface. FIXME if the interface + * association descriptor shows there's more than one function. + */ +static int usb_disable_remote_wakeup(struct usb_device *udev) +{ + if (udev->speed < USB_SPEED_SUPER) + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + else + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE, USB_INTRF_FUNC_SUSPEND, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); @@ -2928,27 +2960,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) * we don't explicitly enable it here. */ if (udev->do_remote_wakeup) { - if (!hub_is_superspeed(hub->hdev)) { - status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); - } else { - /* Assume there's only one function on the USB 3.0 - * device and enable remote wake for the first - * interface. FIXME if the interface association - * descriptor shows there's more than one function. - */ - status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_SET_FEATURE, - USB_RECIP_INTERFACE, - USB_INTRF_FUNC_SUSPEND, - USB_INTRF_FUNC_SUSPEND_RW | - USB_INTRF_FUNC_SUSPEND_LP, - NULL, 0, - USB_CTRL_SET_TIMEOUT); - } + status = usb_enable_remote_wakeup(udev); if (status) { dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", status); @@ -2999,19 +3011,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ - if (udev->do_remote_wakeup) { - if (!hub_is_superspeed(hub->hdev)) { - (void) usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, - USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); - } else - (void) usb_disable_function_remotewakeup(udev); - - } + if (udev->do_remote_wakeup) + (void) usb_disable_remote_wakeup(udev); /* Try to enable USB2 hardware LPM again */ if (udev->usb2_hw_lpm_capable == 1) @@ -3119,22 +3120,15 @@ static int finish_port_resume(struct usb_device *udev) * udev->reset_resume */ } else if (udev->actconfig && !udev->reset_resume) { - if (!hub_is_superspeed(udev->parent)) { + if (udev->speed < USB_SPEED_SUPER) { if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) - status = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, - USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); + status = usb_disable_remote_wakeup(udev); } else { status = usb_get_status(udev, USB_RECIP_INTERFACE, 0, &devstatus); if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW)) - status = - usb_disable_function_remotewakeup(udev); + status = usb_disable_remote_wakeup(udev); } if (status) -- cgit v1.2.3-58-ga151 From 4fae6f0fa86f92e6bc7429371b1e177ad0aaac66 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 30 Jul 2013 15:39:02 -0400 Subject: USB: handle LPM errors during device suspend correctly The hub driver's usb_port_suspend() routine doesn't handle errors related to Link Power Management properly. It always returns failure, it doesn't try to clean up the wakeup setting, (in the case of system sleep) it doesn't try to go ahead with the port suspend regardless, and it doesn't try to apply the new power-off mechanism. This patch fixes these problems. Signed-off-by: Alan Stern Acked-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c376c8bad40c..0416f835cfee 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2948,7 +2948,6 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) { struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); struct usb_port *port_dev = hub->ports[udev->portnum - 1]; - enum pm_qos_flags_status pm_qos_stat; int port1 = udev->portnum; int status; bool really_suspend = true; @@ -2966,7 +2965,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) status); /* bail if autosuspend is requested */ if (PMSG_IS_AUTO(msg)) - return status; + goto err_wakeup; } } @@ -2975,14 +2974,16 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) usb_set_usb2_hardware_lpm(udev, 0); if (usb_disable_ltm(udev)) { - dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.", - __func__); - return -ENOMEM; + dev_err(&udev->dev, "Failed to disable LTM before suspend\n."); + status = -ENOMEM; + if (PMSG_IS_AUTO(msg)) + goto err_ltm; } if (usb_unlocked_disable_lpm(udev)) { - dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", - __func__); - return -ENOMEM; + dev_err(&udev->dev, "Failed to disable LPM before suspend\n."); + status = -ENOMEM; + if (PMSG_IS_AUTO(msg)) + goto err_lpm3; } /* see 7.1.7.6 */ @@ -3010,17 +3011,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (status) { dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); - /* paranoia: "should not happen" */ - if (udev->do_remote_wakeup) - (void) usb_disable_remote_wakeup(udev); + /* Try to enable USB3 LPM and LTM again */ + usb_unlocked_enable_lpm(udev); + err_lpm3: + usb_enable_ltm(udev); + err_ltm: /* Try to enable USB2 hardware LPM again */ if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LTM and LPM again */ - usb_enable_ltm(udev); - usb_unlocked_enable_lpm(udev); + if (udev->do_remote_wakeup) + (void) usb_disable_remote_wakeup(udev); + err_wakeup: /* System sleep transitions should never fail */ if (!PMSG_IS_AUTO(msg)) @@ -3042,14 +3045,15 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) * Check whether current status meets the requirement of * usb port power off mechanism */ - pm_qos_stat = dev_pm_qos_flags(&port_dev->dev, - PM_QOS_FLAG_NO_POWER_OFF); - if (!udev->do_remote_wakeup - && pm_qos_stat != PM_QOS_FLAGS_ALL - && udev->persist_enabled - && !status) { - pm_runtime_put_sync(&port_dev->dev); - port_dev->did_runtime_put = true; + if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled) { + enum pm_qos_flags_status pm_qos_stat; + + pm_qos_stat = dev_pm_qos_flags(&port_dev->dev, + PM_QOS_FLAG_NO_POWER_OFF); + if (pm_qos_stat != PM_QOS_FLAGS_ALL) { + pm_runtime_put_sync(&port_dev->dev); + port_dev->did_runtime_put = true; + } } usb_mark_last_busy(hub->hdev); -- cgit v1.2.3-58-ga151 From d4f09e28d7bc5c1adde8229b1e89401f23fb44f9 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 19:59:40 +0900 Subject: USB: host: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 14 +++++++------- drivers/usb/host/ehci-mv.c | 2 +- drivers/usb/host/ehci-mxc.c | 4 ++-- drivers/usb/host/ehci-omap.c | 4 ++-- drivers/usb/host/ehci-orion.c | 2 +- drivers/usb/host/ehci-platform.c | 12 ++++++------ drivers/usb/host/ehci-s5p.c | 2 +- drivers/usb/host/ehci-sh.c | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ehci-tilegx.c | 4 ++-- drivers/usb/host/fsl-mph-dr-of.c | 4 ++-- drivers/usb/host/imx21-hcd.c | 2 +- drivers/usb/host/isp116x-hcd.c | 2 +- drivers/usb/host/isp1362-hcd.c | 2 +- drivers/usb/host/isp1760-if.c | 2 +- drivers/usb/host/ohci-at91.c | 12 ++++++------ drivers/usb/host/ohci-da8xx.c | 8 ++++---- drivers/usb/host/ohci-exynos.c | 2 +- drivers/usb/host/ohci-omap.c | 4 ++-- drivers/usb/host/ohci-platform.c | 10 +++++----- drivers/usb/host/ohci-pxa27x.c | 8 ++++---- drivers/usb/host/ohci-s3c2410.c | 11 ++++++----- drivers/usb/host/ohci-tilegx.c | 4 ++-- drivers/usb/host/r8a66597-hcd.c | 2 +- drivers/usb/host/sl811-hcd.c | 2 +- drivers/usb/host/u132-hcd.c | 6 +++--- 26 files changed, 65 insertions(+), 64 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index bd831ec06dcd..45eee6e9c6c8 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -57,7 +57,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, pr_debug("initializing FSL-SOC USB Controller\n"); /* Need platform data for setup */ - pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data; + pdata = (struct fsl_usb2_platform_data *)dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "No platform data for %s.\n", dev_name(&pdev->dev)); @@ -190,7 +190,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev) { - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); if (!IS_ERR_OR_NULL(hcd->phy)) { otg_set_host(hcd->phy->otg, NULL); @@ -218,7 +218,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, struct ehci_hcd *ehci = hcd_to_ehci(hcd); void __iomem *non_ehci = hcd->regs; struct device *dev = hcd->self.controller; - struct fsl_usb2_platform_data *pdata = dev->platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(dev); if (pdata->controller_ver < 0) { dev_warn(hcd->self.controller, "Could not get controller version\n"); @@ -291,7 +291,7 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) struct fsl_usb2_platform_data *pdata; void __iomem *non_ehci = hcd->regs; - pdata = hcd->self.controller->platform_data; + pdata = dev_get_platdata(hcd->self.controller); if (pdata->have_sysif_regs) { /* @@ -363,7 +363,7 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) struct device *dev; dev = hcd->self.controller; - pdata = hcd->self.controller->platform_data; + pdata = dev_get_platdata(hcd->self.controller); ehci->big_endian_desc = pdata->big_endian_desc; ehci->big_endian_mmio = pdata->big_endian_mmio; @@ -415,7 +415,7 @@ static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct fsl_usb2_platform_data *pdata = dev->platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(dev); u32 tmp; #ifdef DEBUG @@ -484,7 +484,7 @@ static int ehci_fsl_mpc512x_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct fsl_usb2_platform_data *pdata = dev->platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(dev); u32 tmp; dev_dbg(dev, "suspend=%d already_suspended=%d\n", diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 915c2db96dce..35cdbd88bbbe 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -131,7 +131,7 @@ static const struct hc_driver mv_ehci_hc_driver = { static int mv_ehci_probe(struct platform_device *pdev) { - struct mv_usb_platform_data *pdata = pdev->dev.platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); struct usb_hcd *hcd; struct ehci_hcd *ehci; struct ehci_hcd_mv *ehci_mv; diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 030b53958845..5899ad6626c7 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -49,7 +49,7 @@ static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = { static int ehci_mxc_drv_probe(struct platform_device *pdev) { - struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; + struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev); struct usb_hcd *hcd; struct resource *res; int irq, ret; @@ -174,7 +174,7 @@ err_alloc: static int ehci_mxc_drv_remove(struct platform_device *pdev) { - struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; + struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev); struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv; diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 4384d07115d1..78b01fa475bb 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -100,7 +100,7 @@ static const struct ehci_driver_overrides ehci_omap_overrides __initdata = { static int ehci_hcd_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct usbhs_omap_platform_data *pdata = dev->platform_data; + struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev); struct resource *res; struct usb_hcd *hcd; void __iomem *regs; @@ -119,7 +119,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* For DT boot, get platform data from parent. i.e. usbhshost */ if (dev->of_node) { - pdata = dev->parent->platform_data; + pdata = dev_get_platdata(dev->parent); dev->platform_data = pdata; } diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 1a450aa13ebf..d1dfb9db5b42 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -139,7 +139,7 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, static int ehci_orion_drv_probe(struct platform_device *pdev) { - struct orion_ehci_data *pd = pdev->dev.platform_data; + struct orion_ehci_data *pd = dev_get_platdata(&pdev->dev); const struct mbus_dram_target_info *dram; struct resource *res; struct usb_hcd *hcd; diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 5196d728517d..f6b790ca8cf2 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -39,7 +39,7 @@ static const char hcd_name[] = "ehci-platform"; static int ehci_platform_reset(struct usb_hcd *hcd) { struct platform_device *pdev = to_platform_device(hcd->self.controller); - struct usb_ehci_pdata *pdata = pdev->dev.platform_data; + struct usb_ehci_pdata *pdata = dev_get_platdata(&pdev->dev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; @@ -87,14 +87,14 @@ static int ehci_platform_probe(struct platform_device *dev) * use reasonable defaults so platforms don't have to provide these. * with DT probing on ARM, none of these are set. */ - if (!dev->dev.platform_data) + if (!dev_get_platdata(&dev->dev)) dev->dev.platform_data = &ehci_platform_defaults; if (!dev->dev.dma_mask) dev->dev.dma_mask = &dev->dev.coherent_dma_mask; if (!dev->dev.coherent_dma_mask) dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - pdata = dev->dev.platform_data; + pdata = dev_get_platdata(&dev->dev); irq = platform_get_irq(dev, 0); if (irq < 0) { @@ -148,7 +148,7 @@ err_power: static int ehci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); - struct usb_ehci_pdata *pdata = dev->dev.platform_data; + struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); usb_remove_hcd(hcd); usb_put_hcd(hcd); @@ -167,7 +167,7 @@ static int ehci_platform_remove(struct platform_device *dev) static int ehci_platform_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct usb_ehci_pdata *pdata = dev->platform_data; + struct usb_ehci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = container_of(dev, struct platform_device, dev); bool do_wakeup = device_may_wakeup(dev); @@ -184,7 +184,7 @@ static int ehci_platform_suspend(struct device *dev) static int ehci_platform_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct usb_ehci_pdata *pdata = dev->platform_data; + struct usb_ehci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = container_of(dev, struct platform_device, dev); diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index aba645bd4866..7c3de95c7054 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -75,7 +75,7 @@ static void s5p_setup_vbus_gpio(struct platform_device *pdev) static int s5p_ehci_probe(struct platform_device *pdev) { - struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; + struct s5p_ehci_platdata *pdata = dev_get_platdata(&pdev->dev); struct s5p_ehci_hcd *s5p_ehci; struct usb_hcd *hcd; struct ehci_hcd *ehci; diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index c4c0ee92a397..93e59a13bc1f 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -104,7 +104,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) goto fail_create_hcd; } - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); /* initialize hcd */ hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev, diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 6ee7ef79b4f8..5cd33cf783da 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -361,7 +361,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) struct device_node *np_phy; struct usb_phy *u_phy; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "Platform data missing\n"); return -EINVAL; diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c index d72b2929c03d..cca4be90a864 100644 --- a/drivers/usb/host/ehci-tilegx.c +++ b/drivers/usb/host/ehci-tilegx.c @@ -101,7 +101,7 @@ static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev) { struct usb_hcd *hcd; struct ehci_hcd *ehci; - struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); pte_t pte = { 0 }; int my_cpu = smp_processor_id(); int ret; @@ -186,7 +186,7 @@ err_hcd: static int ehci_hcd_tilegx_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); usb_remove_hcd(hcd); usb_put_hcd(hcd); diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 11e0b79ff9d5..cfbff7161828 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -258,7 +258,7 @@ static int fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev) int fsl_usb2_mpc5121_init(struct platform_device *pdev) { - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); struct clk *clk; char clk_name[10]; int base, clk_num; @@ -298,7 +298,7 @@ int fsl_usb2_mpc5121_init(struct platform_device *pdev) static void fsl_usb2_mpc5121_exit(struct platform_device *pdev) { - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); pdata->regs = NULL; diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index 03dc4d9cbeca..60a5de505ca1 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1860,7 +1860,7 @@ static int imx21_probe(struct platform_device *pdev) imx21 = hcd_to_imx21(hcd); imx21->hcd = hcd; imx21->dev = &pdev->dev; - imx21->pdata = pdev->dev.platform_data; + imx21->pdata = dev_get_platdata(&pdev->dev); if (!imx21->pdata) imx21->pdata = &default_pdata; diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index b64e661618bb..c7d0f8f231be 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1626,7 +1626,7 @@ static int isp116x_probe(struct platform_device *pdev) isp116x->addr_reg = addr_reg; spin_lock_init(&isp116x->lock); INIT_LIST_HEAD(&isp116x->async); - isp116x->board = pdev->dev.platform_data; + isp116x->board = dev_get_platdata(&pdev->dev); if (!isp116x->board) { ERR("Platform data structure not initialized\n"); diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index c15463b898e2..6f29abad6815 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2725,7 +2725,7 @@ static int isp1362_probe(struct platform_device *pdev) INIT_LIST_HEAD(&isp1362_hcd->periodic); INIT_LIST_HEAD(&isp1362_hcd->isoc); INIT_LIST_HEAD(&isp1362_hcd->remove_list); - isp1362_hcd->board = pdev->dev.platform_data; + isp1362_hcd->board = dev_get_platdata(&pdev->dev); #if USE_PLATFORM_DELAY if (!isp1362_hcd->board->delay) { dev_err(hcd->self.controller, "No platform delay function given\n"); diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 3df49b169b53..df931e9ba5b5 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -351,7 +351,7 @@ static int isp1760_plat_probe(struct platform_device *pdev) struct resource *mem_res; struct resource *irq_res; resource_size_t mem_size; - struct isp1760_platform_data *priv = pdev->dev.platform_data; + struct isp1760_platform_data *priv = dev_get_platdata(&pdev->dev); unsigned int devflags = 0; unsigned long irqflags = IRQF_SHARED; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 9677f6831209..a87564559e03 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -225,7 +225,7 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd, static int ohci_at91_reset (struct usb_hcd *hcd) { - struct at91_usbh_data *board = hcd->self.controller->platform_data; + struct at91_usbh_data *board = dev_get_platdata(hcd->self.controller); struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; @@ -280,7 +280,7 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) */ static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) { - struct at91_usbh_data *pdata = hcd->self.controller->platform_data; + struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller); int length = ohci_hub_status_data(hcd, buf); int port; @@ -301,7 +301,7 @@ static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { - struct at91_usbh_data *pdata = hcd->self.controller->platform_data; + struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller); struct usb_hub_descriptor *desc; int ret = -EINVAL; u32 *data = (u32 *)buf; @@ -461,7 +461,7 @@ static const struct hc_driver ohci_at91_hc_driver = { static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) { struct platform_device *pdev = data; - struct at91_usbh_data *pdata = pdev->dev.platform_data; + struct at91_usbh_data *pdata = dev_get_platdata(&pdev->dev); int val, gpio, port; /* From the GPIO notifying the over-current situation, find @@ -567,7 +567,7 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) if (ret) return ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (pdata) { at91_for_each_port(i) { @@ -643,7 +643,7 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) { - struct at91_usbh_data *pdata = pdev->dev.platform_data; + struct at91_usbh_data *pdata = dev_get_platdata(&pdev->dev); int i; if (pdata) { diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 6aaa9c9c8eb0..9be59f11e051 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -85,7 +85,7 @@ static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub, static int ohci_da8xx_init(struct usb_hcd *hcd) { struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev->platform_data; + struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); int result; u32 rh_a; @@ -171,7 +171,7 @@ static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev->platform_data; + struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); int temp; switch (typeReq) { @@ -292,7 +292,7 @@ static const struct hc_driver ohci_da8xx_hc_driver = { static int usb_hcd_da8xx_probe(const struct hc_driver *driver, struct platform_device *pdev) { - struct da8xx_ohci_root_hub *hub = pdev->dev.platform_data; + struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev); struct usb_hcd *hcd; struct resource *mem; int error, irq; @@ -380,7 +380,7 @@ err0: static inline void usb_hcd_da8xx_remove(struct usb_hcd *hcd, struct platform_device *pdev) { - struct da8xx_ohci_root_hub *hub = pdev->dev.platform_data; + struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev); hub->ocic_notify(NULL); usb_remove_hcd(hcd); diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index b0b542c14e31..dc6ee9adacf5 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -100,7 +100,7 @@ static const struct hc_driver exynos_ohci_hc_driver = { static int exynos_ohci_probe(struct platform_device *pdev) { - struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; + struct exynos4_ohci_platdata *pdata = dev_get_platdata(&pdev->dev); struct exynos_ohci_hcd *exynos_ohci; struct usb_hcd *hcd; struct ohci_hcd *ohci; diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 8747fa6a51b7..31d3a12eb486 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -191,7 +191,7 @@ static void start_hnp(struct ohci_hcd *ohci) static int ohci_omap_init(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci(hcd); - struct omap_usb_config *config = hcd->self.controller->platform_data; + struct omap_usb_config *config = dev_get_platdata(hcd->self.controller); int need_transceiver = (config->otg != 0); int ret; @@ -427,7 +427,7 @@ ohci_omap_start (struct usb_hcd *hcd) if (!host_enabled) return 0; - config = hcd->self.controller->platform_data; + config = dev_get_platdata(hcd->self.controller); if (config->otg || config->rwc) { ohci->hc_control = OHCI_CTRL_RWC; writel(OHCI_CTRL_RWC, &ohci->regs->control); diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index bc30475c3a23..a4c6410f0ed4 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -33,7 +33,7 @@ static const char hcd_name[] = "ohci-platform"; static int ohci_platform_reset(struct usb_hcd *hcd) { struct platform_device *pdev = to_platform_device(hcd->self.controller); - struct usb_ohci_pdata *pdata = pdev->dev.platform_data; + struct usb_ohci_pdata *pdata = dev_get_platdata(&pdev->dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); if (pdata->big_endian_desc) @@ -59,7 +59,7 @@ static int ohci_platform_probe(struct platform_device *dev) { struct usb_hcd *hcd; struct resource *res_mem; - struct usb_ohci_pdata *pdata = dev->dev.platform_data; + struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); int irq; int err = -ENOMEM; @@ -124,7 +124,7 @@ err_power: static int ohci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); - struct usb_ohci_pdata *pdata = dev->dev.platform_data; + struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); usb_remove_hcd(hcd); usb_put_hcd(hcd); @@ -139,7 +139,7 @@ static int ohci_platform_remove(struct platform_device *dev) static int ohci_platform_suspend(struct device *dev) { - struct usb_ohci_pdata *pdata = dev->platform_data; + struct usb_ohci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = container_of(dev, struct platform_device, dev); @@ -152,7 +152,7 @@ static int ohci_platform_suspend(struct device *dev) static int ohci_platform_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct usb_ohci_pdata *pdata = dev->platform_data; + struct usb_ohci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = container_of(dev, struct platform_device, dev); diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 3a9c01d8b79c..93371a235e82 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -219,7 +219,7 @@ static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev) struct pxaohci_platform_data *inf; uint32_t uhchr; - inf = dev->platform_data; + inf = dev_get_platdata(dev); clk_prepare_enable(ohci->clk); @@ -256,7 +256,7 @@ static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev) struct pxaohci_platform_data *inf; uint32_t uhccoms; - inf = dev->platform_data; + inf = dev_get_platdata(dev); if (cpu_is_pxa3xx()) pxa3xx_u2d_stop_hc(&ohci_to_hcd(&ohci->ohci)->self); @@ -364,7 +364,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device if (retval) return retval; - inf = pdev->dev.platform_data; + inf = dev_get_platdata(&pdev->dev); if (!inf) return -ENODEV; @@ -577,7 +577,7 @@ static int ohci_hcd_pxa27x_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); - struct pxaohci_platform_data *inf = dev->platform_data; + struct pxaohci_platform_data *inf = dev_get_platdata(dev); int status; if (time_before(jiffies, ohci->ohci.next_statechange)) diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index e125770b893c..4919afa4125e 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -38,12 +38,12 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc); static struct s3c2410_hcd_info *to_s3c2410_info(struct usb_hcd *hcd) { - return hcd->self.controller->platform_data; + return dev_get_platdata(hcd->self.controller); } static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd) { - struct s3c2410_hcd_info *info = dev->dev.platform_data; + struct s3c2410_hcd_info *info = dev_get_platdata(&dev->dev); dev_dbg(&dev->dev, "s3c2410_start_hc:\n"); @@ -63,7 +63,7 @@ static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd) static void s3c2410_stop_hc(struct platform_device *dev) { - struct s3c2410_hcd_info *info = dev->dev.platform_data; + struct s3c2410_hcd_info *info = dev_get_platdata(&dev->dev); dev_dbg(&dev->dev, "s3c2410_stop_hc:\n"); @@ -339,10 +339,11 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, struct platform_device *dev) { struct usb_hcd *hcd = NULL; + struct s3c2410_hcd_info *info = dev_get_platdata(&dev->dev); int retval; - s3c2410_usb_set_power(dev->dev.platform_data, 1, 1); - s3c2410_usb_set_power(dev->dev.platform_data, 2, 1); + s3c2410_usb_set_power(info, 1, 1); + s3c2410_usb_set_power(info, 2, 1); hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx"); if (hcd == NULL) diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c index 197d514fe0d1..22540ab71f55 100644 --- a/drivers/usb/host/ohci-tilegx.c +++ b/drivers/usb/host/ohci-tilegx.c @@ -95,7 +95,7 @@ static const struct hc_driver ohci_tilegx_hc_driver = { static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev) { struct usb_hcd *hcd; - struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); pte_t pte = { 0 }; int my_cpu = smp_processor_id(); int ret; @@ -175,7 +175,7 @@ err_hcd: static int ohci_hcd_tilegx_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct tilegx_usb_platform_data* pdata = pdev->dev.platform_data; + struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); usb_remove_hcd(hcd); usb_put_hcd(hcd); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index a6fd8f5371df..a9eef6822a24 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2467,7 +2467,7 @@ static int r8a66597_probe(struct platform_device *pdev) r8a66597 = hcd_to_r8a66597(hcd); memset(r8a66597, 0, sizeof(struct r8a66597)); dev_set_drvdata(&pdev->dev, r8a66597); - r8a66597->pdata = pdev->dev.platform_data; + r8a66597->pdata = dev_get_platdata(&pdev->dev); r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; if (r8a66597->pdata->on_chip) { diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index d8938630e092..5477bf5df218 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1691,7 +1691,7 @@ sl811h_probe(struct platform_device *dev) spin_lock_init(&sl811->lock); INIT_LIST_HEAD(&sl811->async); - sl811->board = dev->dev.platform_data; + sl811->board = dev_get_platdata(&dev->dev); init_timer(&sl811->timer); sl811->timer.function = sl811h_timer; sl811->timer.data = (unsigned long) sl811; diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 5c124bf5d018..e402beb5a069 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -1809,9 +1809,9 @@ static int u132_hcd_start(struct usb_hcd *hcd) struct platform_device *pdev = to_platform_device(hcd->self.controller); u16 vendor = ((struct u132_platform_data *) - (pdev->dev.platform_data))->vendor; + dev_get_platdata(&pdev->dev))->vendor; u16 device = ((struct u132_platform_data *) - (pdev->dev.platform_data))->device; + dev_get_platdata(&pdev->dev))->device; mutex_lock(&u132->sw_lock); msleep(10); if (vendor == PCI_VENDOR_ID_AMD && device == 0x740c) { @@ -3034,7 +3034,7 @@ static void u132_initialise(struct u132 *u132, struct platform_device *pdev) int addrs = MAX_U132_ADDRS; int udevs = MAX_U132_UDEVS; int endps = MAX_U132_ENDPS; - u132->board = pdev->dev.platform_data; + u132->board = dev_get_platdata(&pdev->dev); u132->platform_dev = pdev; u132->power = 0; u132->reset = 0; -- cgit v1.2.3-58-ga151 From b8bdad608213caffa081a97d2e937e5fe08c4046 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Tue, 30 Jul 2013 21:49:04 +0200 Subject: USB: pl2303: restrict the divisor based baud rate encoding method to the "HX" chip type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's not clear if the type_0 and type_1 chips support the divisor based baud rate encoding method, so don't use it until anyone with such chip has tested it to avoid regressions with the following patches. Even if it has been working fine with these chips since the code has been added 2 years ago, this change will not cause any regressions, because the baud rates currently supported/allowed with the divisor based method are supported with the direct method, too. The code for the divisor based method also isn't entirely correct (yet), so that the direct encoding method actually works better (sets the baud rate more precisely). Signed-off-by: Frank Schäfer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1e6de4cd079d..244820193e10 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -305,10 +305,14 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, if (spriv->type != HX) baud = min_t(int, baud, 1228800); - if (baud <= 115200) { + if (spriv->type != HX || baud <= 115200) { + /* Direct (standard) baud rate encoding method */ put_unaligned_le32(baud, buf); } else { /* + * NOTE: it's not clear if the type_0/1 chips + * support this method + * * Apparently the formula for higher speeds is: * baudrate = 12M * 32 / (2^buf[1]) / buf[0] */ -- cgit v1.2.3-58-ga151 From 3a20446f02bfb71d68ae9ec673268bb7823f878c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 1 Aug 2013 17:09:23 -0400 Subject: USB: EHCI: keep better track of resuming ports The ehci-hcd driver isn't as careful as it should be about the way it uses ehci->resuming_ports. One of the omissions was fixed recently by commit 47a64a13d54 (USB: EHCI: Fix resume signalling on remote wakeup), but there are other places that need attention: When a port's suspend feature is explicitly cleared, the corresponding bit in resuming_ports should be set and the core should be notified about the port resume. We don't need to clear a resuming_ports bit when a reset completes. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 6dce37555c4f..6d0e59306ed9 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -801,6 +801,8 @@ static int ehci_hub_control ( ehci_writel(ehci, temp | PORT_RESUME, status_reg); ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); + set_bit(wIndex, &ehci->resuming_ports); + usb_hcd_start_port_resume(&hcd->self, wIndex); break; case USB_PORT_FEAT_C_SUSPEND: clear_bit(wIndex, &ehci->port_c_suspend); @@ -911,7 +913,6 @@ static int ehci_hub_control ( ehci->reset_done[wIndex])) { status |= USB_PORT_STAT_C_RESET << 16; ehci->reset_done [wIndex] = 0; - clear_bit(wIndex, &ehci->resuming_ports); /* force reset to complete */ ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET), -- cgit v1.2.3-58-ga151 From 6753f4cf29046f4a2ae68c3a93bf6e6e6dce9fb7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 1 Aug 2013 17:09:28 -0400 Subject: USB: EHCI: don't depend on hardware for tracking port resets and resumes In theory, an EHCI controller can turn off the PORT_RESUME or PORT_RESET bits in a port status register all by itself (and some controllers actually do this). We shouldn't depend on these bits being set correctly. This patch rearranges the code in ehci-hcd that handles completion of port resets and resumes. We guarantee that ehci->reset_done[portnum] is nonzero if a reset or resume is in progress, and that the portnum bit is set in ehci->resuming_ports if the operation is a resume. (To help enforce this guarantee, the patch prevents suspended ports from being reset.) Therefore it's not necessary to look at the port status bits to learn what's going on. The patch looks bigger than it really is, because it changes the indentation level of a sizeable region of code. Most of what it actually does is interchange some tests. The only functional changes are testing reset_done and resuming_ports rather than PORT_RESUME and PORT_RESET, removing a now-unnecessary check for spontaneous resets of the PORT_RESUME and PORT_RESET bits, and preventing a suspended or resuming port from being reset. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 60 ++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 34 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 6d0e59306ed9..6e69ee1a3371 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -867,11 +867,11 @@ static int ehci_hub_control ( } } - /* whoever resumes must GetPortStatus to complete it!! */ - if (temp & PORT_RESUME) { + /* no reset or resume pending */ + if (!ehci->reset_done[wIndex]) { /* Remote Wakeup received? */ - if (!ehci->reset_done[wIndex]) { + if (temp & PORT_RESUME) { /* resume signaling for 20 msec */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); @@ -882,35 +882,32 @@ static int ehci_hub_control ( ehci->reset_done[wIndex]); } - /* resume completed? */ - else if (time_after_eq(jiffies, - ehci->reset_done[wIndex])) { - clear_bit(wIndex, &ehci->suspended_ports); - set_bit(wIndex, &ehci->port_c_suspend); - ehci->reset_done[wIndex] = 0; - usb_hcd_end_port_resume(&hcd->self, wIndex); - - /* stop resume signaling */ - temp &= ~(PORT_RWC_BITS | - PORT_SUSPEND | PORT_RESUME); - ehci_writel(ehci, temp, status_reg); - clear_bit(wIndex, &ehci->resuming_ports); - retval = ehci_handshake(ehci, status_reg, - PORT_RESUME, 0, 2000 /* 2msec */); - if (retval != 0) { - ehci_err(ehci, - "port %d resume error %d\n", + /* reset or resume not yet complete */ + } else if (!time_after_eq(jiffies, ehci->reset_done[wIndex])) { + ; /* wait until it is complete */ + + /* resume completed */ + } else if (test_bit(wIndex, &ehci->resuming_ports)) { + clear_bit(wIndex, &ehci->suspended_ports); + set_bit(wIndex, &ehci->port_c_suspend); + ehci->reset_done[wIndex] = 0; + usb_hcd_end_port_resume(&hcd->self, wIndex); + + /* stop resume signaling */ + temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME); + ehci_writel(ehci, temp, status_reg); + clear_bit(wIndex, &ehci->resuming_ports); + retval = ehci_handshake(ehci, status_reg, + PORT_RESUME, 0, 2000 /* 2msec */); + if (retval != 0) { + ehci_err(ehci, "port %d resume error %d\n", wIndex + 1, retval); - goto error; - } - temp = ehci_readl(ehci, status_reg); + goto error; } - } + temp = ehci_readl(ehci, status_reg); /* whoever resets must GetPortStatus to complete it!! */ - if ((temp & PORT_RESET) - && time_after_eq(jiffies, - ehci->reset_done[wIndex])) { + } else { status |= USB_PORT_STAT_C_RESET << 16; ehci->reset_done [wIndex] = 0; @@ -933,11 +930,6 @@ static int ehci_hub_control ( ehci_readl(ehci, status_reg)); } - if (!(temp & (PORT_RESUME|PORT_RESET))) { - ehci->reset_done[wIndex] = 0; - clear_bit(wIndex, &ehci->resuming_ports); - } - /* transfer dedicated ports to the companion hc */ if ((temp & PORT_CONNECT) && test_bit(wIndex, &ehci->companion_ports)) { @@ -1058,7 +1050,7 @@ static int ehci_hub_control ( status_reg); break; case USB_PORT_FEAT_RESET: - if (temp & PORT_RESUME) + if (temp & (PORT_SUSPEND|PORT_RESUME)) goto error; /* line status bits may report this as low speed, * which can be fine if this root hub has a -- cgit v1.2.3-58-ga151 From 6b0a1cf732f917f2eaccb4a0dd6ae7bfc3ccda15 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 1 Aug 2013 19:09:13 +0200 Subject: USB: ohci-at91: add usb_clk for transition to common clk framework The AT91 PMC (Power Management Controller) provides an USB clock used by USB Full Speed host (ohci) and USB Full Speed device (udc). The usb drivers (ohci and udc) must configure this clock to 48Mhz. This configuration was formely done in mach-at91/clock.c, but this implementation will be removed when moving to common clk framework. This patch adds support for usb clock retrieval and configuration, and is backward compatible with the current at91 clk implementation (if usb clk is not found, it does not configure/enable it). Changes since v1: - use IS_ENABLED(CONFIG_COMMON_CLK) to isolate new at91 clk support Signed-off-by: Boris BREZILLON Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index a87564559e03..b3cdd1467521 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -31,8 +31,8 @@ #define at91_for_each_port(index) \ for ((index) = 0; (index) < AT91_MAX_USBH_PORTS; (index)++) -/* interface and function clocks; sometimes also an AHB clock */ -static struct clk *iclk, *fclk, *hclk; +/* interface, function and usb clocks; sometimes also an AHB clock */ +static struct clk *iclk, *fclk, *uclk, *hclk; static int clocked; extern int usb_disabled(void); @@ -41,6 +41,10 @@ extern int usb_disabled(void); static void at91_start_clock(void) { + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + clk_set_rate(uclk, 48000000); + clk_prepare_enable(uclk); + } clk_prepare_enable(hclk); clk_prepare_enable(iclk); clk_prepare_enable(fclk); @@ -52,6 +56,8 @@ static void at91_stop_clock(void) clk_disable_unprepare(fclk); clk_disable_unprepare(iclk); clk_disable_unprepare(hclk); + if (IS_ENABLED(CONFIG_COMMON_CLK)) + clk_disable_unprepare(uclk); clocked = 0; } @@ -162,6 +168,14 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, retval = PTR_ERR(hclk); goto err5; } + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + uclk = clk_get(&pdev->dev, "usb_clk"); + if (IS_ERR(uclk)) { + dev_err(&pdev->dev, "failed to get uclk\n"); + retval = PTR_ERR(uclk); + goto err6; + } + } at91_start_hc(pdev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -173,6 +187,9 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, /* Error handling */ at91_stop_hc(pdev); + if (IS_ENABLED(CONFIG_COMMON_CLK)) + clk_put(uclk); + err6: clk_put(hclk); err5: clk_put(fclk); @@ -212,6 +229,8 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd, release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); + if (IS_ENABLED(CONFIG_COMMON_CLK)) + clk_put(uclk); clk_put(hclk); clk_put(fclk); clk_put(iclk); -- cgit v1.2.3-58-ga151 From fb74d282d29dbb39ef20211a9cf2883f9f593bdd Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 2 Aug 2013 17:13:17 +0900 Subject: usb: dwc3: pci: add CONFIG_PM_SLEEP to suspend/resume functions Add CONFIG_PM_SLEEP to suspend/resume functions to fix the following build warning when CONFIG_PM_SLEEP is not selected. This is because sleep PM callbacks defined by SET_SYSTEM_SLEEP_PM_OPS are only used when the CONFIG_PM_SLEEP is enabled. Unnecessary CONFIG_PM ifdefs are removed. drivers/usb/dwc3/dwc3-pci.c:215:12: warning: 'dwc3_pci_suspend' defined but not used [-Wunused-function] drivers/usb/dwc3/dwc3-pci.c:224:12: warning: 'dwc3_pci_resume' defined but not used [-Wunused-function] Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 5d746e5d6a0a..105535abcc4a 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -191,7 +191,7 @@ static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { }; MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int dwc3_pci_suspend(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); @@ -216,23 +216,19 @@ static int dwc3_pci_resume(struct device *dev) return 0; } +#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops dwc3_pci_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume) }; -#define DEV_PM_OPS (&dwc3_pci_dev_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif /* CONFIG_PM */ - static struct pci_driver dwc3_pci_driver = { .name = "dwc3-pci", .id_table = dwc3_pci_id_table, .probe = dwc3_pci_probe, .remove = dwc3_pci_remove, .driver = { - .pm = DEV_PM_OPS, + .pm = &dwc3_pci_dev_pm_ops, }, }; -- cgit v1.2.3-58-ga151 From 027ca0d2fe779faf19c7c3414f840fdc851af41d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 2 Aug 2013 17:16:33 +0900 Subject: usb: gadget: goku_udc: use NULL instead of 0 'req' is a pointer; thus, use NULL instead of 0 to fix the following sparse warning: drivers/usb/gadget/goku_udc.c:775:13: warning: Using plain integer as NULL pointer Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/goku_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 52dd6cc6c0aa..c64deb9e3d62 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -772,7 +772,7 @@ goku_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) } /* else pio or dma irq handler advances the queue. */ - if (likely(req != 0)) + if (likely(req != NULL)) list_add_tail(&req->queue, &ep->queue); if (likely(!list_empty(&ep->queue)) -- cgit v1.2.3-58-ga151 From 93487aa1639d2151820f3988f62005d0a00e9269 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 2 Aug 2013 17:17:05 +0900 Subject: usb: gadget: fusb300_udc: Staticize fusb300_rdcxf() fusb300_rdcxf() used only in this file. Fix the following sparse warning: drivers/usb/gadget/fusb300_udc.c:560:6: warning: symbol 'fusb300_rdcxf' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/fusb300_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index c83f3e165325..f1dd6daabe21 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -557,7 +557,7 @@ static void fusb300_set_cxdone(struct fusb300 *fusb300) } /* read data from cx fifo */ -void fusb300_rdcxf(struct fusb300 *fusb300, +static void fusb300_rdcxf(struct fusb300 *fusb300, u8 *buffer, u32 length) { int i = 0; -- cgit v1.2.3-58-ga151 From 30ce1987160e64f58d92359ee11999e1218a45b9 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 2 Aug 2013 10:37:27 +0200 Subject: usb: gadget: at91_udc: add missing clk_put on fclk and iclk This patch adds missing clk_put on fclk and iclk in case the probe function fails after these clocks have been retrieved. Signed-off-by: Boris BREZILLON Signed-off-by: Felipe Balbi --- drivers/usb/gadget/at91_udc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index d237429dd93a..94d1d3cacce8 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1851,6 +1851,10 @@ fail3: fail2: free_irq(udc->udp_irq, udc); fail1: + if (!IS_ERR(udc->fclk)) + clk_put(udc->fclk); + if (!IS_ERR(udc->iclk)) + clk_put(udc->iclk); iounmap(udc->udp_baseaddr); fail0a: if (cpu_is_at91rm9200()) -- cgit v1.2.3-58-ga151 From c0aefc75ce2512bc308a89ca3cb7bece63c1128f Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 2 Aug 2013 10:37:34 +0200 Subject: usb: gadget: at91_udc: add usb_clk for transition to common clk framework The AT91 PMC (Power Management Controller) provides an USB clock used by USB Full Speed host (ohci) and USB Full Speed device (udc). The usb drivers (ohci and udc) must configure this clock to 48Mhz. This configuration was formely done in mach-at91/clock.c, but this implementation will be removed when moving to common clk framework. This patch adds support for usb clock retrieval and configuration, and is backward compatible with the current at91 clk implementation (if usb clk is not found, it does not configure/enable it). Signed-off-by: Boris BREZILLON Signed-off-by: Felipe Balbi --- drivers/usb/gadget/at91_udc.c | 17 +++++++++++++++-- drivers/usb/gadget/at91_udc.h | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 94d1d3cacce8..4cc4fd6d1473 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -870,6 +870,11 @@ static void clk_on(struct at91_udc *udc) if (udc->clocked) return; udc->clocked = 1; + + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + clk_set_rate(udc->uclk, 48000000); + clk_prepare_enable(udc->uclk); + } clk_prepare_enable(udc->iclk); clk_prepare_enable(udc->fclk); } @@ -882,6 +887,8 @@ static void clk_off(struct at91_udc *udc) udc->gadget.speed = USB_SPEED_UNKNOWN; clk_disable_unprepare(udc->fclk); clk_disable_unprepare(udc->iclk); + if (IS_ENABLED(CONFIG_COMMON_CLK)) + clk_disable_unprepare(udc->uclk); } /* @@ -1774,10 +1781,12 @@ static int at91udc_probe(struct platform_device *pdev) /* get interface and function clocks */ udc->iclk = clk_get(dev, "udc_clk"); udc->fclk = clk_get(dev, "udpck"); - if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk)) { + if (IS_ENABLED(CONFIG_COMMON_CLK)) + udc->uclk = clk_get(dev, "usb_clk"); + if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk) || + (IS_ENABLED(CONFIG_COMMON_CLK) && IS_ERR(udc->uclk))) { DBG("clocks missing\n"); retval = -ENODEV; - /* NOTE: we "know" here that refcounts on these are NOPs */ goto fail1; } @@ -1851,6 +1860,8 @@ fail3: fail2: free_irq(udc->udp_irq, udc); fail1: + if (IS_ENABLED(CONFIG_COMMON_CLK) && !IS_ERR(udc->uclk)) + clk_put(udc->uclk); if (!IS_ERR(udc->fclk)) clk_put(udc->fclk); if (!IS_ERR(udc->iclk)) @@ -1898,6 +1909,8 @@ static int __exit at91udc_remove(struct platform_device *pdev) clk_put(udc->iclk); clk_put(udc->fclk); + if (IS_ENABLED(CONFIG_COMMON_CLK)) + clk_put(udc->uclk); return 0; } diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index e647d1c2ada4..017524663381 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -126,7 +126,7 @@ struct at91_udc { unsigned active_suspend:1; u8 addr; struct at91_udc_data board; - struct clk *iclk, *fclk; + struct clk *iclk, *fclk, *uclk; struct platform_device *pdev; struct proc_dir_entry *pde; void __iomem *udp_baseaddr; -- cgit v1.2.3-58-ga151 From 626f090c5cbbe557379978c7a9525011ad7fbbf6 Mon Sep 17 00:00:00 2001 From: Yacine Belkadi Date: Fri, 2 Aug 2013 20:10:04 +0200 Subject: usb: fix some scripts/kernel-doc warnings When building the htmldocs (in verbose mode), scripts/kernel-doc reports the following type of warnings: Warning(drivers/usb/core/usb.c:76): No description found for return value of 'usb_find_alt_setting' Fix them by: - adding some missing descriptions of return values - using "Return" sections for those descriptions Signed-off-by: Yacine Belkadi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/buffer.c | 5 ++-- drivers/usb/core/driver.c | 22 +++++++++++++++-- drivers/usb/core/file.c | 2 +- drivers/usb/core/hcd-pci.c | 2 ++ drivers/usb/core/hcd.c | 37 ++++++++++++++++++++-------- drivers/usb/core/hub.c | 37 +++++++++++++++++++--------- drivers/usb/core/message.c | 49 ++++++++++++++++++++----------------- drivers/usb/core/urb.c | 34 ++++++++++++++++---------- drivers/usb/core/usb.c | 60 ++++++++++++++++++++++++++++------------------ include/linux/usb.h | 12 ++++++---- 10 files changed, 173 insertions(+), 87 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index b0585e623ba9..23559746be92 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -43,10 +43,11 @@ static const size_t pool_max[HCD_BUFFER_POOLS] = { * * Call this as part of initializing a host controller that uses the dma * memory allocators. It initializes some pools of dma-coherent memory that - * will be shared by all drivers using that controller, or returns a negative - * errno value on error. + * will be shared by all drivers using that controller. * * Call hcd_buffer_destroy() to clean up after using those pools. + * + * Return: 0 if successful. A negative errno value otherwise. */ int hcd_buffer_create(struct usb_hcd *hcd) { diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 7609ac4aed1c..124bcc50b0b7 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -117,6 +117,8 @@ static DRIVER_ATTR(new_id, S_IRUGO | S_IWUSR, show_dynids, store_new_id); * @count: input size * * Removes a dynamic usb device ID from this driver. + * + * Return: @count on success. A negative error code otherwise. */ static ssize_t store_remove_id(struct device_driver *driver, const char *buf, size_t count) @@ -457,6 +459,8 @@ static int usb_unbind_interface(struct device *dev) * Callers must own the device lock, so driver probe() entries don't need * extra locking, but other call contexts may need to explicitly claim that * lock. + * + * Return: 0 on success. */ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *priv) @@ -658,6 +662,8 @@ EXPORT_SYMBOL_GPL(usb_match_one_id); * These device tables are exported with MODULE_DEVICE_TABLE, through * modutils, to support the driver loading functionality of USB hotplugging. * + * Return: The first matching usb_device_id, or %NULL. + * * What Matches: * * The "match_flags" element in a usb_device_id controls which @@ -823,7 +829,8 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) * Registers a USB device driver with the USB core. The list of * unattached devices will be rescanned whenever a new driver is * added, allowing the new driver to attach to any recognized devices. - * Returns a negative error code on failure and 0 on success. + * + * Return: A negative error code on failure and 0 on success. */ int usb_register_device_driver(struct usb_device_driver *new_udriver, struct module *owner) @@ -879,7 +886,8 @@ EXPORT_SYMBOL_GPL(usb_deregister_device_driver); * Registers a USB interface driver with the USB core. The list of * unattached interfaces will be rescanned whenever a new driver is * added, allowing the new driver to attach to any recognized interfaces. - * Returns a negative error code on failure and 0 on success. + * + * Return: A negative error code on failure and 0 on success. * * NOTE: if you want your driver to use the USB major number, you must call * usb_register_dev() to enable that functionality. This function no longer @@ -1213,6 +1221,8 @@ done: * unpredictable times. * * This routine can run only in process context. + * + * Return: 0 if the suspend succeeded. */ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) { @@ -1294,6 +1304,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * unpredictable times. * * This routine can run only in process context. + * + * Return: 0 on success. */ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) { @@ -1491,6 +1503,8 @@ void usb_autosuspend_device(struct usb_device *udev) * The caller must hold @udev's device lock. * * This routine can run only in process context. + * + * Return: 0 on success. A negative error code otherwise. */ int usb_autoresume_device(struct usb_device *udev) { @@ -1600,6 +1614,8 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface_no_suspend); * However if the autoresume fails then the counter is re-decremented. * * This routine can run only in process context. + * + * Return: 0 on success. */ int usb_autopm_get_interface(struct usb_interface *intf) { @@ -1633,6 +1649,8 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface); * resumed. * * This routine can run in atomic context. + * + * Return: 0 on success. A negative error code otherwise. */ int usb_autopm_get_interface_async(struct usb_interface *intf) { diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 6a4c40766f0f..7421888087a3 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -153,7 +153,7 @@ void usb_major_cleanup(void) * usb_deregister_dev() must be called when the driver is done with * the minor numbers given out by this function. * - * Returns -EINVAL if something bad happens with trying to register a + * Return: -EINVAL if something bad happens with trying to register a * device, and 0 on success. */ int usb_register_dev(struct usb_interface *intf, diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index caeb8d6d39fb..b9d3c43e3859 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -171,6 +171,8 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd, * through the hotplug entry's driver_data. * * Store this function in the HCD's struct pci_driver as probe(). + * + * Return: 0 if successful. */ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 014dc996b4f6..dc1346fee76c 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -378,9 +378,10 @@ MODULE_PARM_DESC(authorized_default, * @buf: Buffer for USB string descriptor (header + UTF-16LE) * @len: Length (in bytes; may be odd) of descriptor buffer. * - * The return value is the number of bytes filled in: 2 + 2*strlen(s) or - * buflen, whichever is less. + * Return: The number of bytes filled in: 2 + 2*strlen(s) or @len, + * whichever is less. * + * Note: * USB String descriptors can contain at most 126 characters; input * strings longer than that are truncated. */ @@ -416,7 +417,8 @@ ascii2desc(char const *s, u8 *buf, unsigned len) * * Produces either a manufacturer, product or serial number string for the * virtual root hub device. - * Returns the number of bytes filled in: the length of the descriptor or + * + * Return: The number of bytes filled in: the length of the descriptor or * of the provided buffer, whichever is less. */ static unsigned @@ -938,6 +940,8 @@ static void usb_bus_init (struct usb_bus *bus) * * Assigns a bus number, and links the controller into usbcore data * structures so that it can be seen by scanning the bus list. + * + * Return: 0 if successful. A negative error code otherwise. */ static int usb_register_bus(struct usb_bus *bus) { @@ -1002,6 +1006,8 @@ static void usb_deregister_bus (struct usb_bus *bus) * the device properly in the device tree and then calls usb_new_device() * to register the usb device. It also assigns the root hub's USB address * (always 1). + * + * Return: 0 if successful. A negative error code otherwise. */ static int register_root_hub(struct usb_hcd *hcd) { @@ -1108,7 +1114,9 @@ EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume); * @isoc: true for isochronous transactions, false for interrupt ones * @bytecount: how many bytes in the transaction. * - * Returns approximate bus time in nanoseconds for a periodic transaction. + * Return: Approximate bus time in nanoseconds for a periodic transaction. + * + * Note: * See USB 2.0 spec section 5.11.3; only periodic transfers need to be * scheduled in software, this function is only used for such scheduling. */ @@ -1166,7 +1174,7 @@ EXPORT_SYMBOL_GPL(usb_calc_bus_time); * be disabled. The actions carried out here are required for URB * submission, as well as for endpoint shutdown and for usb_kill_urb. * - * Returns 0 for no error, otherwise a negative error code (in which case + * Return: 0 for no error, otherwise a negative error code (in which case * the enqueue() method must fail). If no error occurs but enqueue() fails * anyway, it must call usb_hcd_unlink_urb_from_ep() before releasing * the private spinlock and returning. @@ -1221,7 +1229,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_link_urb_to_ep); * be disabled. The actions carried out here are required for making * sure than an unlink is valid. * - * Returns 0 for no error, otherwise a negative error code (in which case + * Return: 0 for no error, otherwise a negative error code (in which case * the dequeue() method must fail). The possible error codes are: * * -EIDRM: @urb was not submitted or has already completed. @@ -1784,7 +1792,7 @@ rescan: * pass in the current alternate interface setting in cur_alt, * and pass in the new alternate interface setting in new_alt. * - * Returns an error if the requested bandwidth change exceeds the + * Return: An error if the requested bandwidth change exceeds the * bus bandwidth or host controller internal resources. */ int usb_hcd_alloc_bandwidth(struct usb_device *udev, @@ -1954,9 +1962,12 @@ void usb_hcd_reset_endpoint(struct usb_device *udev, * @num_streams: number of streams to allocate. * @mem_flags: flags hcd should use to allocate memory. * - * Sets up a group of bulk endpoints to have num_streams stream IDs available. + * Sets up a group of bulk endpoints to have @num_streams stream IDs available. * Drivers may queue multiple transfers to different stream IDs, which may * complete in a different order than they were queued. + * + * Return: On success, the number of allocated streams. On failure, a negative + * error code. */ int usb_alloc_streams(struct usb_interface *interface, struct usb_host_endpoint **eps, unsigned int num_eps, @@ -2201,6 +2212,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); * khubd identifying and possibly configuring the device. * This is needed by OTG controller drivers, where it helps meet * HNP protocol timing requirements for starting a port reset. + * + * Return: 0 if successful. */ int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num) { @@ -2235,6 +2248,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum); * * If the controller isn't HALTed, calls the driver's irq handler. * Checks whether the controller is now dead. + * + * Return: %IRQ_HANDLED if the IRQ was handled. %IRQ_NONE otherwise. */ irqreturn_t usb_hcd_irq (int irq, void *__hcd) { @@ -2320,7 +2335,8 @@ EXPORT_SYMBOL_GPL (usb_hc_died); * HC driver's private data. Initialize the generic members of the * hcd structure. * - * If memory is unavailable, returns NULL. + * Return: On success, a pointer to the created and initialized HCD structure. + * On failure (e.g. if memory is unavailable), %NULL. */ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name, @@ -2384,7 +2400,8 @@ EXPORT_SYMBOL_GPL(usb_create_shared_hcd); * HC driver's private data. Initialize the generic members of the * hcd structure. * - * If memory is unavailable, returns NULL. + * Return: On success, a pointer to the created and initialized HCD + * structure. On failure (e.g. if memory is unavailable), %NULL. */ struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0416f835cfee..5d6d28a76e0a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -734,6 +734,8 @@ static void hub_tt_work(struct work_struct *work) * * call this function to control port's power via setting or * clearing the port's PORT_POWER feature. + * + * Return: 0 if successful. A negative error code otherwise. */ int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub, int port1, bool set) @@ -762,6 +764,8 @@ int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub, * * It may not be possible for that hub to handle additional full (or low) * speed transactions until that state is fully cleared out. + * + * Return: 0 if successful. A negative error code otherwise. */ int usb_hub_clear_tt_buffer(struct urb *urb) { @@ -964,6 +968,8 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) * see that the device has been disconnected. When the device is * physically unplugged and something is plugged in, the events will * be received and processed normally. + * + * Return: 0 if successful. A negative error code otherwise. */ int usb_remove_device(struct usb_device *udev) { @@ -2115,6 +2121,8 @@ static inline void announce_device(struct usb_device *udev) { } * @udev: newly addressed device (in ADDRESS state) * * Finish enumeration for On-The-Go devices + * + * Return: 0 if successful. A negative error code otherwise. */ static int usb_enumerate_device_otg(struct usb_device *udev) { @@ -2197,6 +2205,8 @@ fail: * If the device is WUSB and not authorized, we don't attempt to read * the string descriptors, as they will be errored out by the device * until it has been authorized. + * + * Return: 0 if successful. A negative error code otherwise. */ static int usb_enumerate_device(struct usb_device *udev) { @@ -2277,13 +2287,14 @@ static void set_usb_port_removable(struct usb_device *udev) * udev has already been installed, but udev is not yet visible through * sysfs or other filesystem code. * - * It will return if the device is configured properly or not. Zero if - * the interface was registered with the driver core; else a negative - * errno value. - * * This call is synchronous, and may not be used in an interrupt context. * * Only the hub driver or root-hub registrar should ever call this. + * + * Return: Whether the device is configured properly or not. Zero if the + * interface was registered with the driver core; else a negative errno + * value. + * */ int usb_new_device(struct usb_device *udev) { @@ -2391,6 +2402,8 @@ fail: * * We share a lock (that we have) with device_del(), so we need to * defer its call. + * + * Return: 0. */ int usb_deauthorize_device(struct usb_device *usb_dev) { @@ -5025,10 +5038,11 @@ static int descriptors_changed(struct usb_device *udev, * re-connected. All drivers will be unbound, and the device will be * re-enumerated and probed all over again. * - * Returns 0 if the reset succeeded, -ENODEV if the device has been + * Return: 0 if the reset succeeded, -ENODEV if the device has been * flagged for logical disconnection, or some other negative error code * if the reset wasn't even attempted. * + * Note: * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock @@ -5184,8 +5198,9 @@ re_enumerate: * method), performs the port reset, and then lets the drivers know that * the reset is over (using their post_reset method). * - * Return value is the same as for usb_reset_and_verify_device(). + * Return: The same as for usb_reset_and_verify_device(). * + * Note: * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock @@ -5323,7 +5338,7 @@ EXPORT_SYMBOL_GPL(usb_queue_reset_device); * USB drivers call this function to get hub's child device * pointer. * - * Return NULL if input param is invalid and + * Return: %NULL if input param is invalid and * child's usb_device pointer if non-NULL. */ struct usb_device *usb_hub_find_child(struct usb_device *hdev, @@ -5357,8 +5372,8 @@ void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1, * @hdev: USB device belonging to the usb hub * @port1: port num of the port * - * Return connect type of the port and if input params are - * invalid, return USB_PORT_CONNECT_TYPE_UNKNOWN. + * Return: The connect type of the port if successful. Or + * USB_PORT_CONNECT_TYPE_UNKNOWN if input params are invalid. */ enum usb_port_connect_type usb_get_hub_port_connect_type(struct usb_device *hdev, int port1) @@ -5418,8 +5433,8 @@ void usb_hub_adjust_deviceremovable(struct usb_device *hdev, * @hdev: USB device belonging to the usb hub * @port1: port num of the port * - * Return port's acpi handle if successful, NULL if params are - * invaild. + * Return: Port's acpi handle if successful, %NULL if params are + * invalid. */ acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev, int port1) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 6549a975b0c5..82927e1ed27d 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -119,15 +119,15 @@ static int usb_internal_control_msg(struct usb_device *usb_dev, * This function sends a simple control message to a specified endpoint and * waits for the message to complete, or timeout. * - * If successful, it returns the number of bytes transferred, otherwise a - * negative error number. - * * Don't use this function from within an interrupt context, like a bottom half * handler. If you need an asynchronous message, or need to send a message * from within interrupt context, use usb_submit_urb(). * If a thread in your driver uses this call, make sure your disconnect() * method can wait for it to complete. Since you don't have a handle on the * URB used, you can't cancel the request. + * + * Return: If successful, the number of bytes transferred. Otherwise, a negative + * error number. */ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, @@ -170,15 +170,16 @@ EXPORT_SYMBOL_GPL(usb_control_msg); * This function sends a simple interrupt message to a specified endpoint and * waits for the message to complete, or timeout. * - * If successful, it returns 0, otherwise a negative error number. The number - * of actual bytes transferred will be stored in the actual_length paramater. - * * Don't use this function from within an interrupt context, like a bottom half * handler. If you need an asynchronous message, or need to send a message * from within interrupt context, use usb_submit_urb() If a thread in your * driver uses this call, make sure your disconnect() method can wait for it to * complete. Since you don't have a handle on the URB used, you can't cancel * the request. + * + * Return: + * If successful, 0. Otherwise a negative error number. The number of actual + * bytes transferred will be stored in the @actual_length paramater. */ int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) @@ -203,9 +204,6 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg); * This function sends a simple bulk message to a specified endpoint * and waits for the message to complete, or timeout. * - * If successful, it returns 0, otherwise a negative error number. The number - * of actual bytes transferred will be stored in the actual_length paramater. - * * Don't use this function from within an interrupt context, like a bottom half * handler. If you need an asynchronous message, or need to send a message * from within interrupt context, use usb_submit_urb() If a thread in your @@ -217,6 +215,11 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg); * users are forced to abuse this routine by using it to submit URBs for * interrupt endpoints. We will take the liberty of creating an interrupt URB * (with the default interval) if the target is an interrupt endpoint. + * + * Return: + * If successful, 0. Otherwise a negative error number. The number of actual + * bytes transferred will be stored in the @actual_length paramater. + * */ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) @@ -341,9 +344,9 @@ static void sg_complete(struct urb *urb) * send every byte identified in the list. * @mem_flags: SLAB_* flags affecting memory allocations in this call * - * Returns zero for success, else a negative errno value. This initializes a - * scatter/gather request, allocating resources such as I/O mappings and urb - * memory (except maybe memory used by USB controller drivers). + * This initializes a scatter/gather request, allocating resources such as + * I/O mappings and urb memory (except maybe memory used by USB controller + * drivers). * * The request must be issued using usb_sg_wait(), which waits for the I/O to * complete (or to be canceled) and then cleans up all resources allocated by @@ -351,6 +354,8 @@ static void sg_complete(struct urb *urb) * * The request may be canceled with usb_sg_cancel(), either before or after * usb_sg_wait() is called. + * + * Return: Zero for success, else a negative errno value. */ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, unsigned pipe, unsigned period, struct scatterlist *sg, @@ -623,7 +628,7 @@ EXPORT_SYMBOL_GPL(usb_sg_cancel); * * This call is synchronous, and may not be used in an interrupt context. * - * Returns the number of bytes received on success, or else the status code + * Return: The number of bytes received on success, or else the status code * returned by the underlying usb_control_msg() call. */ int usb_get_descriptor(struct usb_device *dev, unsigned char type, @@ -671,7 +676,7 @@ EXPORT_SYMBOL_GPL(usb_get_descriptor); * * This call is synchronous, and may not be used in an interrupt context. * - * Returns the number of bytes received on success, or else the status code + * Return: The number of bytes received on success, or else the status code * returned by the underlying usb_control_msg() call. */ static int usb_get_string(struct usb_device *dev, unsigned short langid, @@ -805,7 +810,7 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) * * This call is synchronous, and may not be used in an interrupt context. * - * Returns length of the string (>= 0) or usb_control_msg status (< 0). + * Return: length of the string (>= 0) or usb_control_msg status (< 0). */ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) { @@ -853,8 +858,8 @@ EXPORT_SYMBOL_GPL(usb_string); * @udev: the device whose string descriptor is being read * @index: the descriptor index * - * Returns a pointer to a kmalloc'ed buffer containing the descriptor string, - * or NULL if the index is 0 or the string could not be read. + * Return: A pointer to a kmalloc'ed buffer containing the descriptor string, + * or %NULL if the index is 0 or the string could not be read. */ char *usb_cache_string(struct usb_device *udev, int index) { @@ -894,7 +899,7 @@ char *usb_cache_string(struct usb_device *udev, int index) * * This call is synchronous, and may not be used in an interrupt context. * - * Returns the number of bytes received on success, or else the status code + * Return: The number of bytes received on success, or else the status code * returned by the underlying usb_control_msg() call. */ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) @@ -980,7 +985,7 @@ EXPORT_SYMBOL_GPL(usb_get_status); * * This call is synchronous, and may not be used in an interrupt context. * - * Returns zero on success, or else the status code returned by the + * Return: Zero on success, or else the status code returned by the * underlying usb_control_msg() call. */ int usb_clear_halt(struct usb_device *dev, int pipe) @@ -1277,7 +1282,7 @@ void usb_enable_interface(struct usb_device *dev, * endpoints in that interface; all such urbs must first be completed * (perhaps forced by unlinking). * - * Returns zero on success, or else the status code returned by the + * Return: Zero on success, or else the status code returned by the * underlying usb_control_msg() call. */ int usb_set_interface(struct usb_device *dev, int interface, int alternate) @@ -1431,7 +1436,7 @@ EXPORT_SYMBOL_GPL(usb_set_interface); * * The caller must own the device lock. * - * Returns zero on success, else a negative error code. + * Return: Zero on success, else a negative error code. */ int usb_reset_configuration(struct usb_device *dev) { @@ -1973,7 +1978,7 @@ static void cancel_async_set_config(struct usb_device *udev) * routine gets around the normal restrictions by using a work thread to * submit the change-config request. * - * Returns 0 if the request was successfully queued, error code otherwise. + * Return: 0 if the request was successfully queued, error code otherwise. * The caller has no way to know whether the queued request will eventually * succeed. */ diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index e75115a04a2e..4e7a311ff6d7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -55,12 +55,12 @@ EXPORT_SYMBOL_GPL(usb_init_urb); * Creates an urb for the USB driver to use, initializes a few internal * structures, incrementes the usage counter, and returns a pointer to it. * - * If no memory is available, NULL is returned. - * * If the driver want to use this urb for interrupt, control, or bulk * endpoints, pass '0' as the number of iso packets. * * The driver must call usb_free_urb() when it is finished with the urb. + * + * Return: A pointer to the new urb, or %NULL if no memory is available. */ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags) { @@ -103,7 +103,7 @@ EXPORT_SYMBOL_GPL(usb_free_urb); * host controller driver. This allows proper reference counting to happen * for urbs. * - * A pointer to the urb with the incremented reference counter is returned. + * Return: A pointer to the urb with the incremented reference counter. */ struct urb *usb_get_urb(struct urb *urb) { @@ -200,13 +200,12 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); * the particular kind of transfer, although they will not initialize * any transfer flags. * - * Successful submissions return 0; otherwise this routine returns a - * negative error number. If the submission is successful, the complete() - * callback from the URB will be called exactly once, when the USB core and - * Host Controller Driver (HCD) are finished with the URB. When the completion - * function is called, control of the URB is returned to the device - * driver which issued the request. The completion handler may then - * immediately free or reuse that URB. + * If the submission is successful, the complete() callback from the URB + * will be called exactly once, when the USB core and Host Controller Driver + * (HCD) are finished with the URB. When the completion function is called, + * control of the URB is returned to the device driver which issued the + * request. The completion handler may then immediately free or reuse that + * URB. * * With few exceptions, USB device drivers should never access URB fields * provided by usbcore or the HCD until its complete() is called. @@ -241,6 +240,9 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); * that are standardized in the USB 2.0 specification. For bulk * endpoints, a synchronous usb_bulk_msg() call is available. * + * Return: + * 0 on successful submissions. A negative error number otherwise. + * * Request Queuing: * * URBs may be submitted to endpoints before previous ones complete, to @@ -572,6 +574,9 @@ EXPORT_SYMBOL_GPL(usb_submit_urb); * particular, when a driver calls this routine, it must insure that the * completion handler cannot deallocate the URB. * + * Return: -EINPROGRESS on success. See description for other values on + * failure. + * * Unlinking and Endpoint Queues: * * [The behaviors and guarantees described below do not apply to virtual @@ -846,6 +851,8 @@ EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs); * * Call this is you want to be sure all an anchor's * URBs have finished + * + * Return: Non-zero if the anchor became unused. Zero on timeout. */ int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, unsigned int timeout) @@ -859,8 +866,11 @@ EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout); * usb_get_from_anchor - get an anchor's oldest urb * @anchor: the anchor whose urb you want * - * this will take the oldest urb from an anchor, + * This will take the oldest urb from an anchor, * unanchor and return it + * + * Return: The oldest urb from @anchor, or %NULL if @anchor has no + * urbs associated with it. */ struct urb *usb_get_from_anchor(struct usb_anchor *anchor) { @@ -909,7 +919,7 @@ EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs); * usb_anchor_empty - is an anchor empty * @anchor: the anchor you want to query * - * returns 1 if the anchor has no urbs associated with it + * Return: 1 if the anchor has no urbs associated with it. */ int usb_anchor_empty(struct usb_anchor *anchor) { diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 7dad603dde43..0a6ee2e70b25 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -68,6 +68,8 @@ MODULE_PARM_DESC(autosuspend, "default autosuspend delay"); * @alt_num: alternate interface setting number to search for. * * Search the configuration's interface cache for the given alt setting. + * + * Return: The alternate setting, if found. %NULL otherwise. */ struct usb_host_interface *usb_find_alt_setting( struct usb_host_config *config, @@ -103,8 +105,7 @@ EXPORT_SYMBOL_GPL(usb_find_alt_setting); * @ifnum: the desired interface * * This walks the device descriptor for the currently active configuration - * and returns a pointer to the interface with that particular interface - * number, or null. + * to find the interface object with the particular interface number. * * Note that configuration descriptors are not required to assign interface * numbers sequentially, so that it would be incorrect to assume that @@ -115,6 +116,9 @@ EXPORT_SYMBOL_GPL(usb_find_alt_setting); * * Don't call this function unless you are bound to one of the interfaces * on this device or you have locked the device! + * + * Return: A pointer to the interface that has @ifnum as interface number, + * if found. %NULL otherwise. */ struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev, unsigned ifnum) @@ -139,8 +143,7 @@ EXPORT_SYMBOL_GPL(usb_ifnum_to_if); * @altnum: the desired alternate setting number * * This searches the altsetting array of the specified interface for - * an entry with the correct bAlternateSetting value and returns a pointer - * to that entry, or null. + * an entry with the correct bAlternateSetting value. * * Note that altsettings need not be stored sequentially by number, so * it would be incorrect to assume that the first altsetting entry in @@ -149,6 +152,9 @@ EXPORT_SYMBOL_GPL(usb_ifnum_to_if); * * Don't call this function unless you are bound to the intf interface * or you have locked the device! + * + * Return: A pointer to the entry of the altsetting array of @intf that + * has @altnum as the alternate setting number. %NULL if not found. */ struct usb_host_interface *usb_altnum_to_altsetting( const struct usb_interface *intf, @@ -191,6 +197,8 @@ static int __find_interface(struct device *dev, void *data) * This walks the bus device list and returns a pointer to the interface * with the matching minor and driver. Note, this only works for devices * that share the USB major number. + * + * Return: A pointer to the interface with the matching major and @minor. */ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) { @@ -390,6 +398,9 @@ static unsigned usb_bus_is_wusb(struct usb_bus *bus) * controllers) should ever call this. * * This call may not be used in a non-sleeping context. + * + * Return: On success, a pointer to the allocated usb device. %NULL on + * failure. */ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) @@ -501,7 +512,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, * their probe() methods, when they bind to an interface, and release * them by calling usb_put_dev(), in their disconnect() methods. * - * A pointer to the device with the incremented reference counter is returned. + * Return: A pointer to the device with the incremented reference counter. */ struct usb_device *usb_get_dev(struct usb_device *dev) { @@ -535,8 +546,7 @@ EXPORT_SYMBOL_GPL(usb_put_dev); * their probe() methods, when they bind to an interface, and release * them by calling usb_put_intf(), in their disconnect() methods. * - * A pointer to the interface with the incremented reference counter is - * returned. + * Return: A pointer to the interface with the incremented reference counter. */ struct usb_interface *usb_get_intf(struct usb_interface *intf) { @@ -589,7 +599,7 @@ EXPORT_SYMBOL_GPL(usb_put_intf); * disconnect; in some drivers (such as usb-storage) the disconnect() * or suspend() method will block waiting for a device reset to complete. * - * Returns a negative error code for failure, otherwise 0. + * Return: A negative error code for failure, otherwise 0. */ int usb_lock_device_for_reset(struct usb_device *udev, const struct usb_interface *iface) @@ -628,14 +638,15 @@ EXPORT_SYMBOL_GPL(usb_lock_device_for_reset); * usb_get_current_frame_number - return current bus frame number * @dev: the device whose bus is being queried * - * Returns the current frame number for the USB host controller - * used with the given USB device. This can be used when scheduling + * Return: The current frame number for the USB host controller used + * with the given USB device. This can be used when scheduling * isochronous requests. * - * Note that different kinds of host controller have different - * "scheduling horizons". While one type might support scheduling only - * 32 frames into the future, others could support scheduling up to - * 1024 frames into the future. + * Note: Different kinds of host controller have different "scheduling + * horizons". While one type might support scheduling only 32 frames + * into the future, others could support scheduling up to 1024 frames + * into the future. + * */ int usb_get_current_frame_number(struct usb_device *dev) { @@ -685,11 +696,12 @@ EXPORT_SYMBOL_GPL(__usb_get_extra_descriptor); * @mem_flags: affect whether allocation may block * @dma: used to return DMA address of buffer * - * Return value is either null (indicating no buffer could be allocated), or - * the cpu-space pointer to a buffer that may be used to perform DMA to the + * Return: Either null (indicating no buffer could be allocated), or the + * cpu-space pointer to a buffer that may be used to perform DMA to the * specified device. Such cpu-space buffers are returned along with the DMA * address (through the pointer provided). * + * Note: * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags * to avoid behaviors like using "DMA bounce buffers", or thrashing IOMMU * hardware during URB completion/resubmit. The implementation varies between @@ -735,17 +747,18 @@ EXPORT_SYMBOL_GPL(usb_free_coherent); * usb_buffer_map - create DMA mapping(s) for an urb * @urb: urb whose transfer_buffer/setup_packet will be mapped * - * Return value is either null (indicating no buffer could be mapped), or - * the parameter. URB_NO_TRANSFER_DMA_MAP is - * added to urb->transfer_flags if the operation succeeds. If the device - * is connected to this system through a non-DMA controller, this operation - * always succeeds. + * URB_NO_TRANSFER_DMA_MAP is added to urb->transfer_flags if the operation + * succeeds. If the device is connected to this system through a non-DMA + * controller, this operation always succeeds. * * This call would normally be used for an urb which is reused, perhaps * as the target of a large periodic transfer, with usb_buffer_dmasync() * calls to synchronize memory and dma state. * * Reverse the effect of this call with usb_buffer_unmap(). + * + * Return: Either %NULL (indicating no buffer could be mapped), or @urb. + * */ #if 0 struct urb *usb_buffer_map(struct urb *urb) @@ -850,9 +863,10 @@ EXPORT_SYMBOL_GPL(usb_buffer_unmap); * @sg: the scatterlist to map * @nents: the number of entries in the scatterlist * - * Return value is either < 0 (indicating no buffers could be mapped), or - * the number of DMA mapping array entries in the scatterlist. + * Return: Either < 0 (indicating no buffers could be mapped), or the + * number of DMA mapping array entries in the scatterlist. * + * Note: * The caller is responsible for placing the resulting DMA addresses from * the scatterlist into URB transfer buffer pointers, and for setting the * URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs. diff --git a/include/linux/usb.h b/include/linux/usb.h index 6efdab089014..acb1b0aa77d8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -708,7 +708,10 @@ extern int usb_driver_claim_interface(struct usb_driver *driver, * usb_interface_claimed - returns true iff an interface is claimed * @iface: the interface being checked * - * Returns true (nonzero) iff the interface is claimed, else false (zero). + * Return: %true (nonzero) iff the interface is claimed, else %false + * (zero). + * + * Note: * Callers must own the driver model's usb bus readlock. So driver * probe() entries don't need extra locking, but other call contexts * may need to explicitly claim that lock. @@ -745,8 +748,9 @@ extern struct usb_host_interface *usb_find_alt_setting( * @buf: where to put the string * @size: how big is "buf"? * - * Returns length of the string (> 0) or negative if size was too small. + * Return: Length of the string (> 0) or negative if size was too small. * + * Note: * This identifier is intended to be "stable", reflecting physical paths in * hardware such as physical bus addresses for host controllers or ports on * USB hubs. That makes it stay the same until systems are physically @@ -1578,7 +1582,7 @@ extern int usb_anchor_empty(struct usb_anchor *anchor); * usb_urb_dir_in - check if an URB describes an IN transfer * @urb: URB to be checked * - * Returns 1 if @urb describes an IN transfer (device-to-host), + * Return: 1 if @urb describes an IN transfer (device-to-host), * otherwise 0. */ static inline int usb_urb_dir_in(struct urb *urb) @@ -1590,7 +1594,7 @@ static inline int usb_urb_dir_in(struct urb *urb) * usb_urb_dir_out - check if an URB describes an OUT transfer * @urb: URB to be checked * - * Returns 1 if @urb describes an OUT transfer (host-to-device), + * Return: 1 if @urb describes an OUT transfer (host-to-device), * otherwise 0. */ static inline int usb_urb_dir_out(struct urb *urb) -- cgit v1.2.3-58-ga151 From ffcba5a510af99e9d443b7e577a9cb0515c919e1 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Thu, 1 Aug 2013 23:50:47 +0400 Subject: usb: gadget: amd5536udc: unconditionally use GFP_ATOMIC in udc_queue() As far as prep_dma() is called with spinlock held, we have to pass GFP_ATOMIC regardless of gfp argument. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Felipe Balbi --- drivers/usb/gadget/amd5536udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index f52dcfe8f545..a9a4346c83aa 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -1122,7 +1122,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) goto finished; } if (ep->dma) { - retval = prep_dma(ep, req, gfp); + retval = prep_dma(ep, req, GFP_ATOMIC); if (retval != 0) goto finished; /* write desc pointer to enable DMA */ @@ -1190,7 +1190,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) * for PPB modes, because of chain creation reasons */ if (ep->in) { - retval = prep_dma(ep, req, gfp); + retval = prep_dma(ep, req, GFP_ATOMIC); if (retval != 0) goto finished; } -- cgit v1.2.3-58-ga151 From 136c489b1cdcaad8f3da31298b839b5861d9de6d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 5 Aug 2013 12:11:05 +0900 Subject: usb: gadget: f_mass_storage: use NULL instead of 0 The local variables such as 'filename', 'vendor_name', and 'product_name' are pointers; thus, use NULL instead of 0 to fix the following sparse warnings drivers/usb/gadget/f_mass_storage.c:3046:27: warning: Using plain integer as NULL pointer drivers/usb/gadget/f_mass_storage.c:3050:28: warning: Using plain integer as NULL pointer drivers/usb/gadget/f_mass_storage.c:3051:29: warning: Using plain integer as NULL pointer Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_mass_storage.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 56f1fd1cba25..4d4e96a5d2e9 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -3043,12 +3043,12 @@ fsg_config_from_params(struct fsg_config *cfg, lun->filename = params->file_count > i && params->file[i][0] ? params->file[i] - : 0; + : NULL; } /* Let MSF use defaults */ - cfg->vendor_name = 0; - cfg->product_name = 0; + cfg->vendor_name = NULL; + cfg->product_name = NULL; cfg->ops = NULL; cfg->private_data = NULL; -- cgit v1.2.3-58-ga151 From bf19647d8f19e89bdf94eef387cfc3825bb48d7f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 5 Aug 2013 12:12:42 +0900 Subject: usb: gadget: rndis: Staticize rndis_init()/rndis_exit() rndis_init() and rndis_exit() are used only in this file. Fix the following sparse warnings: drivers/usb/gadget/rndis.c:1145:5: warning: symbol 'rndis_init' was not declared. Should it be static? drivers/usb/gadget/rndis.c:1179:6: warning: symbol 'rndis_exit' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/rndis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 3e3ea7203030..9575085ded81 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1142,7 +1142,7 @@ static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ -int rndis_init(void) +static int rndis_init(void) { u8 i; @@ -1176,7 +1176,7 @@ int rndis_init(void) } module_init(rndis_init); -void rndis_exit(void) +static void rndis_exit(void) { #ifdef CONFIG_USB_GADGET_DEBUG_FILES u8 i; -- cgit v1.2.3-58-ga151 From 2419831c375d466fcf41dfe02799e5750c25f765 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 5 Aug 2013 12:14:17 +0900 Subject: usb: gadget: u_uac1: add __user annotation Added __user annotation to fix the following sparse warning. drivers/usb/gadget/u_uac1.c:194:52: warning: incorrect type in argument 2 (different address spaces) drivers/usb/gadget/u_uac1.c:194:52: expected void const [noderef] *buf drivers/usb/gadget/u_uac1.c:194:52: got void *buf Acked-by: Jassi Brar Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/u_uac1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/u_uac1.c index c7d460f43390..7a55fea43430 100644 --- a/drivers/usb/gadget/u_uac1.c +++ b/drivers/usb/gadget/u_uac1.c @@ -191,7 +191,7 @@ try_again: frames = bytes_to_frames(runtime, count); old_fs = get_fs(); set_fs(KERNEL_DS); - result = snd_pcm_lib_write(snd->substream, buf, frames); + result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames); if (result != frames) { ERROR(card, "Playback error: %d\n", (int)result); set_fs(old_fs); -- cgit v1.2.3-58-ga151 From cf1dea73c72ded6e1e1f61598a69b663d4c163ed Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 5 Aug 2013 12:15:27 +0900 Subject: usb: gadget: f_uac1: Staticize local functions control_selector_init() is used only in this file. audio_bind_config() is used only in audio.c file to which f_uac1.c is included. Thus, these functions are staticized to fix the following warnings. drivers/usb/gadget/f_uac1.c:698:12: warning: symbol 'control_selector_init' was not declared. Should it be static? drivers/usb/gadget/f_uac1.c:722:12: warning: symbol 'audio_bind_config' was not declared. Should it be static? Acked-by: Jassi Brar Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_uac1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c index fa8ea4ea00c1..2b4c82d84bfc 100644 --- a/drivers/usb/gadget/f_uac1.c +++ b/drivers/usb/gadget/f_uac1.c @@ -695,7 +695,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) } /* Todo: add more control selecotor dynamically */ -int __init control_selector_init(struct f_audio *audio) +static int __init control_selector_init(struct f_audio *audio) { INIT_LIST_HEAD(&audio->cs); list_add(&feature_unit.list, &audio->cs); @@ -719,7 +719,7 @@ int __init control_selector_init(struct f_audio *audio) * * Returns zero on success, else negative errno. */ -int __init audio_bind_config(struct usb_configuration *c) +static int __init audio_bind_config(struct usb_configuration *c) { struct f_audio *audio; int status; -- cgit v1.2.3-58-ga151 From 1e0f20bea2b08775eb832e8f95d320cddd65737b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 5 Aug 2013 14:17:11 +0900 Subject: usb: phy: mv-u3d: Staticize mv_u3d_phy_shutdown() mv_u3d_phy_shutdown() is used only in this file. Fix the following sparse warning: drivers/usb/phy/phy-mv-u3d-usb.c:85:6: warning: symbol 'mv_u3d_phy_shutdown' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mv-u3d-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mv-u3d-usb.c b/drivers/usb/phy/phy-mv-u3d-usb.c index 6688023021d3..d317903022bf 100644 --- a/drivers/usb/phy/phy-mv-u3d-usb.c +++ b/drivers/usb/phy/phy-mv-u3d-usb.c @@ -82,7 +82,7 @@ static void mv_u3d_phy_write(void __iomem *base, u32 reg, u32 value) writel_relaxed(value, data); } -void mv_u3d_phy_shutdown(struct usb_phy *phy) +static void mv_u3d_phy_shutdown(struct usb_phy *phy) { struct mv_u3d_phy *mv_u3d_phy; void __iomem *base; -- cgit v1.2.3-58-ga151 From d07f4a8200cfc7ff2ba23ef87dd6267a4d70fb10 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 5 Aug 2013 14:17:53 +0900 Subject: usb: phy: mv-usb: remove incorrect __exit_p annotation When platform_driver_probe() is not used, bind/unbind via sysfs is enabled. Thus, __exit_p annotation should be removed from remove(). Also, mv_otg_remove() is staticized, because this function is used only in this file. Fix the following sparse warning: drivers/usb/phy/phy-mv-usb.c:656:5: warning: symbol 'mv_otg_remove' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mv-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index 03d16ff24c02..98f6ac6a78ea 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -653,7 +653,7 @@ static struct attribute_group inputs_attr_group = { .attrs = inputs_attrs, }; -int mv_otg_remove(struct platform_device *pdev) +static int mv_otg_remove(struct platform_device *pdev) { struct mv_otg *mvotg = platform_get_drvdata(pdev); @@ -893,7 +893,7 @@ static int mv_otg_resume(struct platform_device *pdev) static struct platform_driver mv_otg_driver = { .probe = mv_otg_probe, - .remove = __exit_p(mv_otg_remove), + .remove = mv_otg_remove, .driver = { .owner = THIS_MODULE, .name = driver_name, -- cgit v1.2.3-58-ga151 From 3fa4d7344be0afebd80382ffeea6b1787cccf971 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 26 Jul 2013 12:16:42 +0200 Subject: usb: phy: rename nop_usb_xceiv => usb_phy_gen_xceiv The "nop" driver isn't a do-nothing-stub but supports a couple functions like clock on/off or is able to use a voltage regulator. This patch simply renames the driver to "generic" since it is easy possible to extend it by a simple function istead of writing a complete driver. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/mach-omap2/board-omap3beagle.c | 4 +- arch/arm/mach-omap2/board-omap3evm.c | 4 +- arch/arm/mach-omap2/board-omap3pandora.c | 2 +- arch/arm/mach-omap2/usb-host.c | 10 +- drivers/usb/dwc3/dwc3-exynos.c | 8 +- drivers/usb/dwc3/dwc3-pci.c | 8 +- drivers/usb/musb/am35x.c | 2 +- drivers/usb/musb/blackfin.c | 2 +- drivers/usb/musb/da8xx.c | 2 +- drivers/usb/musb/davinci.c | 2 +- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/phy/Makefile | 2 +- drivers/usb/phy/phy-generic.c | 292 +++++++++++++++++++++++++++++++ drivers/usb/phy/phy-nop.c | 292 ------------------------------- include/linux/usb/nop-usb-xceiv.h | 29 --- include/linux/usb/usb_phy_gen_xceiv.h | 29 +++ 17 files changed, 346 insertions(+), 346 deletions(-) create mode 100644 drivers/usb/phy/phy-generic.c delete mode 100644 drivers/usb/phy/phy-nop.c delete mode 100644 include/linux/usb/nop-usb-xceiv.h create mode 100644 include/linux/usb/usb_phy_gen_xceiv.h (limited to 'drivers/usb') diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 04c116555412..1c6ae5f5bae7 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -279,7 +279,7 @@ static struct regulator_consumer_supply beagle_vsim_supply[] = { static struct gpio_led gpio_leds[]; /* PHY's VCC regulator might be added later, so flag that we need it */ -static struct nop_usb_xceiv_platform_data hsusb2_phy_data = { +static struct usb_phy_gen_xceiv_platform_data hsusb2_phy_data = { .needs_vcc = true, }; diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index 8c026269baca..52bdddd41e0e 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -468,7 +468,7 @@ struct wl12xx_platform_data omap3evm_wlan_data __initdata = { static struct regulator_consumer_supply omap3evm_vaux2_supplies[] = { REGULATOR_SUPPLY("VDD_CSIPHY1", "omap3isp"), /* OMAP ISP */ REGULATOR_SUPPLY("VDD_CSIPHY2", "omap3isp"), /* OMAP ISP */ - REGULATOR_SUPPLY("vcc", "nop_usb_xceiv.2"), /* hsusb port 2 */ + REGULATOR_SUPPLY("vcc", "usb_phy_gen_xceiv.2"), /* hsusb port 2 */ REGULATOR_SUPPLY("vaux2", NULL), }; diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index b1547a0edfcd..d2b455e70486 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -352,7 +352,7 @@ static struct regulator_consumer_supply pandora_vcc_lcd_supply[] = { }; static struct regulator_consumer_supply pandora_usb_phy_supply[] = { - REGULATOR_SUPPLY("vcc", "nop_usb_xceiv.2"), /* hsusb port 2 */ + REGULATOR_SUPPLY("vcc", "usb_phy_gen_xceiv.2"), /* hsusb port 2 */ }; /* ads7846 on SPI and 2 nub controllers on I2C */ diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c index 2eb19d4d0aa1..e83a6a4b184a 100644 --- a/arch/arm/mach-omap2/usb-host.c +++ b/arch/arm/mach-omap2/usb-host.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "soc.h" #include "omap_device.h" @@ -349,7 +349,7 @@ static struct fixed_voltage_config hsusb_reg_config = { /* .init_data filled later */ }; -static const char *nop_name = "nop_usb_xceiv"; /* NOP PHY driver */ +static const char *nop_name = "usb_phy_gen_xceiv"; /* NOP PHY driver */ static const char *reg_name = "reg-fixed-voltage"; /* Regulator driver */ /** @@ -460,9 +460,9 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys) pdevinfo.name = nop_name; pdevinfo.id = phy->port; pdevinfo.data = phy->platform_data; - pdevinfo.size_data = sizeof(struct nop_usb_xceiv_platform_data); - - scnprintf(phy_id, MAX_STR, "nop_usb_xceiv.%d", + pdevinfo.size_data = + sizeof(struct usb_phy_gen_xceiv_platform_data); + scnprintf(phy_id, MAX_STR, "usb_phy_gen_xceiv.%d", phy->port); pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) { diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 8ce9d7fd6cfc..a179c5a54b39 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -34,13 +34,13 @@ struct dwc3_exynos { static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) { - struct nop_usb_xceiv_platform_data pdata; + struct usb_phy_gen_xceiv_platform_data pdata; struct platform_device *pdev; int ret; memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); + pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) return -ENOMEM; @@ -51,7 +51,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) if (ret) goto err1; - pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); + pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) { ret = -ENOMEM; goto err1; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index ed07ec04a962..87ea3d503fc5 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -43,7 +43,7 @@ #include #include -#include +#include /* FIXME define these in */ #define PCI_VENDOR_ID_SYNOPSYS 0x16c3 @@ -58,13 +58,13 @@ struct dwc3_pci { static int dwc3_pci_register_phys(struct dwc3_pci *glue) { - struct nop_usb_xceiv_platform_data pdata; + struct usb_phy_gen_xceiv_platform_data pdata; struct platform_device *pdev; int ret; memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("nop_usb_xceiv", 0); + pdev = platform_device_alloc("usb_phy_gen_xceiv", 0); if (!pdev) return -ENOMEM; @@ -75,7 +75,7 @@ static int dwc3_pci_register_phys(struct dwc3_pci *glue) if (ret) goto err1; - pdev = platform_device_alloc("nop_usb_xceiv", 1); + pdev = platform_device_alloc("usb_phy_gen_xceiv", 1); if (!pdev) { ret = -ENOMEM; goto err1; diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 2231850c0625..5733a209ab95 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include "musb_core.h" diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 6ba8439bd5a6..195e96674fff 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 0da6f648a9fe..ccc6b63bc380 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index f8aeaf2e2cd1..91f300e93cf4 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 5233804d66b1..55fe3c292cd2 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 2c06a8969a9f..20f182c6184a 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include "musb_core.h" diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 070eca3af18b..24c5816409fd 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -14,7 +14,7 @@ phy-fsl-usb2-objs := phy-fsl-usb.o phy-fsm-usb.o obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb2.o obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o -obj-$(CONFIG_NOP_USB_XCEIV) += phy-nop.o +obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c new file mode 100644 index 000000000000..f379b7ded037 --- /dev/null +++ b/drivers/usb/phy/phy-generic.c @@ -0,0 +1,292 @@ +/* + * drivers/usb/otg/nop-usb-xceiv.c + * + * NOP USB transceiver for all USB transceiver which are either built-in + * into USB IP or which are mostly autonomous. + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Ajay Kumar Gupta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Current status: + * This provides a "nop" transceiver for PHYs which are + * autonomous such as isp1504, isp1707, etc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct usb_phy_gen_xceiv { + struct usb_phy phy; + struct device *dev; + struct clk *clk; + struct regulator *vcc; + struct regulator *reset; +}; + +static struct platform_device *pd; + +void usb_nop_xceiv_register(void) +{ + if (pd) + return; + pd = platform_device_register_simple("usb_phy_gen_xceiv", -1, NULL, 0); + if (!pd) { + pr_err("Unable to register generic usb transceiver\n"); + return; + } +} +EXPORT_SYMBOL(usb_nop_xceiv_register); + +void usb_nop_xceiv_unregister(void) +{ + platform_device_unregister(pd); + pd = NULL; +} +EXPORT_SYMBOL(usb_nop_xceiv_unregister); + +static int nop_set_suspend(struct usb_phy *x, int suspend) +{ + return 0; +} + +static int nop_init(struct usb_phy *phy) +{ + struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->vcc)) { + if (regulator_enable(nop->vcc)) + dev_err(phy->dev, "Failed to enable power\n"); + } + + if (!IS_ERR(nop->clk)) + clk_enable(nop->clk); + + if (!IS_ERR(nop->reset)) { + /* De-assert RESET */ + if (regulator_enable(nop->reset)) + dev_err(phy->dev, "Failed to de-assert reset\n"); + } + + return 0; +} + +static void nop_shutdown(struct usb_phy *phy) +{ + struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->reset)) { + /* Assert RESET */ + if (regulator_disable(nop->reset)) + dev_err(phy->dev, "Failed to assert reset\n"); + } + + if (!IS_ERR(nop->clk)) + clk_disable(nop->clk); + + if (!IS_ERR(nop->vcc)) { + if (regulator_disable(nop->vcc)) + dev_err(phy->dev, "Failed to disable power\n"); + } +} + +static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) +{ + if (!otg) + return -ENODEV; + + if (!gadget) { + otg->gadget = NULL; + return -ENODEV; + } + + otg->gadget = gadget; + otg->phy->state = OTG_STATE_B_IDLE; + return 0; +} + +static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!host) { + otg->host = NULL; + return -ENODEV; + } + + otg->host = host; + return 0; +} + +static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_phy_gen_xceiv_platform_data *pdata = pdev->dev.platform_data; + struct usb_phy_gen_xceiv *nop; + enum usb_phy_type type = USB_PHY_TYPE_USB2; + int err; + u32 clk_rate = 0; + bool needs_vcc = false; + bool needs_reset = false; + + nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); + if (!nop) + return -ENOMEM; + + nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), + GFP_KERNEL); + if (!nop->phy.otg) + return -ENOMEM; + + if (dev->of_node) { + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node, "clock-frequency", &clk_rate)) + clk_rate = 0; + + needs_vcc = of_property_read_bool(node, "vcc-supply"); + needs_reset = of_property_read_bool(node, "reset-supply"); + + } else if (pdata) { + type = pdata->type; + clk_rate = pdata->clk_rate; + needs_vcc = pdata->needs_vcc; + needs_reset = pdata->needs_reset; + } + + nop->clk = devm_clk_get(&pdev->dev, "main_clk"); + if (IS_ERR(nop->clk)) { + dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", + PTR_ERR(nop->clk)); + } + + if (!IS_ERR(nop->clk) && clk_rate) { + err = clk_set_rate(nop->clk, clk_rate); + if (err) { + dev_err(&pdev->dev, "Error setting clock rate\n"); + return err; + } + } + + if (!IS_ERR(nop->clk)) { + err = clk_prepare(nop->clk); + if (err) { + dev_err(&pdev->dev, "Error preparing clock\n"); + return err; + } + } + + nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); + if (IS_ERR(nop->vcc)) { + dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", + PTR_ERR(nop->vcc)); + if (needs_vcc) + return -EPROBE_DEFER; + } + + nop->reset = devm_regulator_get(&pdev->dev, "reset"); + if (IS_ERR(nop->reset)) { + dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", + PTR_ERR(nop->reset)); + if (needs_reset) + return -EPROBE_DEFER; + } + + nop->dev = &pdev->dev; + nop->phy.dev = nop->dev; + nop->phy.label = "nop-xceiv"; + nop->phy.set_suspend = nop_set_suspend; + nop->phy.init = nop_init; + nop->phy.shutdown = nop_shutdown; + nop->phy.state = OTG_STATE_UNDEFINED; + nop->phy.type = type; + + nop->phy.otg->phy = &nop->phy; + nop->phy.otg->set_host = nop_set_host; + nop->phy.otg->set_peripheral = nop_set_peripheral; + + err = usb_add_phy_dev(&nop->phy); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_add; + } + + platform_set_drvdata(pdev, nop); + + ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); + + return 0; + +err_add: + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + return err; +} + +static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) +{ + struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); + + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + + usb_remove_phy(&nop->phy); + + return 0; +} + +static const struct of_device_id nop_xceiv_dt_ids[] = { + { .compatible = "usb-nop-xceiv" }, + { } +}; + +MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); + +static struct platform_driver usb_phy_gen_xceiv_driver = { + .probe = usb_phy_gen_xceiv_probe, + .remove = usb_phy_gen_xceiv_remove, + .driver = { + .name = "usb_phy_gen_xceiv", + .owner = THIS_MODULE, + .of_match_table = nop_xceiv_dt_ids, + }, +}; + +static int __init usb_phy_gen_xceiv_init(void) +{ + return platform_driver_register(&usb_phy_gen_xceiv_driver); +} +subsys_initcall(usb_phy_gen_xceiv_init); + +static void __exit usb_phy_gen_xceiv_exit(void) +{ + platform_driver_unregister(&usb_phy_gen_xceiv_driver); +} +module_exit(usb_phy_gen_xceiv_exit); + +MODULE_ALIAS("platform:usb_phy_gen_xceiv"); +MODULE_AUTHOR("Texas Instruments Inc"); +MODULE_DESCRIPTION("NOP USB Transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-nop.c b/drivers/usb/phy/phy-nop.c deleted file mode 100644 index 55445e5d72e5..000000000000 --- a/drivers/usb/phy/phy-nop.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * drivers/usb/otg/nop-usb-xceiv.c - * - * NOP USB transceiver for all USB transceiver which are either built-in - * into USB IP or which are mostly autonomous. - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Ajay Kumar Gupta - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Current status: - * This provides a "nop" transceiver for PHYs which are - * autonomous such as isp1504, isp1707, etc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct nop_usb_xceiv { - struct usb_phy phy; - struct device *dev; - struct clk *clk; - struct regulator *vcc; - struct regulator *reset; -}; - -static struct platform_device *pd; - -void usb_nop_xceiv_register(void) -{ - if (pd) - return; - pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); - if (!pd) { - printk(KERN_ERR "Unable to register usb nop transceiver\n"); - return; - } -} -EXPORT_SYMBOL(usb_nop_xceiv_register); - -void usb_nop_xceiv_unregister(void) -{ - platform_device_unregister(pd); - pd = NULL; -} -EXPORT_SYMBOL(usb_nop_xceiv_unregister); - -static int nop_set_suspend(struct usb_phy *x, int suspend) -{ - return 0; -} - -static int nop_init(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->vcc)) { - if (regulator_enable(nop->vcc)) - dev_err(phy->dev, "Failed to enable power\n"); - } - - if (!IS_ERR(nop->clk)) - clk_enable(nop->clk); - - if (!IS_ERR(nop->reset)) { - /* De-assert RESET */ - if (regulator_enable(nop->reset)) - dev_err(phy->dev, "Failed to de-assert reset\n"); - } - - return 0; -} - -static void nop_shutdown(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->reset)) { - /* Assert RESET */ - if (regulator_disable(nop->reset)) - dev_err(phy->dev, "Failed to assert reset\n"); - } - - if (!IS_ERR(nop->clk)) - clk_disable(nop->clk); - - if (!IS_ERR(nop->vcc)) { - if (regulator_disable(nop->vcc)) - dev_err(phy->dev, "Failed to disable power\n"); - } -} - -static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) -{ - if (!otg) - return -ENODEV; - - if (!gadget) { - otg->gadget = NULL; - return -ENODEV; - } - - otg->gadget = gadget; - otg->phy->state = OTG_STATE_B_IDLE; - return 0; -} - -static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!host) { - otg->host = NULL; - return -ENODEV; - } - - otg->host = host; - return 0; -} - -static int nop_usb_xceiv_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; - struct nop_usb_xceiv *nop; - enum usb_phy_type type = USB_PHY_TYPE_USB2; - int err; - u32 clk_rate = 0; - bool needs_vcc = false; - bool needs_reset = false; - - nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); - if (!nop) - return -ENOMEM; - - nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), - GFP_KERNEL); - if (!nop->phy.otg) - return -ENOMEM; - - if (dev->of_node) { - struct device_node *node = dev->of_node; - - if (of_property_read_u32(node, "clock-frequency", &clk_rate)) - clk_rate = 0; - - needs_vcc = of_property_read_bool(node, "vcc-supply"); - needs_reset = of_property_read_bool(node, "reset-supply"); - - } else if (pdata) { - type = pdata->type; - clk_rate = pdata->clk_rate; - needs_vcc = pdata->needs_vcc; - needs_reset = pdata->needs_reset; - } - - nop->clk = devm_clk_get(&pdev->dev, "main_clk"); - if (IS_ERR(nop->clk)) { - dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", - PTR_ERR(nop->clk)); - } - - if (!IS_ERR(nop->clk) && clk_rate) { - err = clk_set_rate(nop->clk, clk_rate); - if (err) { - dev_err(&pdev->dev, "Error setting clock rate\n"); - return err; - } - } - - if (!IS_ERR(nop->clk)) { - err = clk_prepare(nop->clk); - if (err) { - dev_err(&pdev->dev, "Error preparing clock\n"); - return err; - } - } - - nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); - if (IS_ERR(nop->vcc)) { - dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", - PTR_ERR(nop->vcc)); - if (needs_vcc) - return -EPROBE_DEFER; - } - - nop->reset = devm_regulator_get(&pdev->dev, "reset"); - if (IS_ERR(nop->reset)) { - dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", - PTR_ERR(nop->reset)); - if (needs_reset) - return -EPROBE_DEFER; - } - - nop->dev = &pdev->dev; - nop->phy.dev = nop->dev; - nop->phy.label = "nop-xceiv"; - nop->phy.set_suspend = nop_set_suspend; - nop->phy.init = nop_init; - nop->phy.shutdown = nop_shutdown; - nop->phy.state = OTG_STATE_UNDEFINED; - nop->phy.type = type; - - nop->phy.otg->phy = &nop->phy; - nop->phy.otg->set_host = nop_set_host; - nop->phy.otg->set_peripheral = nop_set_peripheral; - - err = usb_add_phy_dev(&nop->phy); - if (err) { - dev_err(&pdev->dev, "can't register transceiver, err: %d\n", - err); - goto err_add; - } - - platform_set_drvdata(pdev, nop); - - ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); - - return 0; - -err_add: - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - return err; -} - -static int nop_usb_xceiv_remove(struct platform_device *pdev) -{ - struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - - usb_remove_phy(&nop->phy); - - return 0; -} - -static const struct of_device_id nop_xceiv_dt_ids[] = { - { .compatible = "usb-nop-xceiv" }, - { } -}; - -MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); - -static struct platform_driver nop_usb_xceiv_driver = { - .probe = nop_usb_xceiv_probe, - .remove = nop_usb_xceiv_remove, - .driver = { - .name = "nop_usb_xceiv", - .owner = THIS_MODULE, - .of_match_table = nop_xceiv_dt_ids, - }, -}; - -static int __init nop_usb_xceiv_init(void) -{ - return platform_driver_register(&nop_usb_xceiv_driver); -} -subsys_initcall(nop_usb_xceiv_init); - -static void __exit nop_usb_xceiv_exit(void) -{ - platform_driver_unregister(&nop_usb_xceiv_driver); -} -module_exit(nop_usb_xceiv_exit); - -MODULE_ALIAS("platform:nop_usb_xceiv"); -MODULE_AUTHOR("Texas Instruments Inc"); -MODULE_DESCRIPTION("NOP USB Transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/nop-usb-xceiv.h b/include/linux/usb/nop-usb-xceiv.h deleted file mode 100644 index 148d35171aac..000000000000 --- a/include/linux/usb/nop-usb-xceiv.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __LINUX_USB_NOP_XCEIV_H -#define __LINUX_USB_NOP_XCEIV_H - -#include - -struct nop_usb_xceiv_platform_data { - enum usb_phy_type type; - unsigned long clk_rate; - - /* if set fails with -EPROBE_DEFER if can't get regulator */ - unsigned int needs_vcc:1; - unsigned int needs_reset:1; -}; - -#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) -/* sometimes transceivers are accessed only through e.g. ULPI */ -extern void usb_nop_xceiv_register(void); -extern void usb_nop_xceiv_unregister(void); -#else -static inline void usb_nop_xceiv_register(void) -{ -} - -static inline void usb_nop_xceiv_unregister(void) -{ -} -#endif - -#endif /* __LINUX_USB_NOP_XCEIV_H */ diff --git a/include/linux/usb/usb_phy_gen_xceiv.h b/include/linux/usb/usb_phy_gen_xceiv.h new file mode 100644 index 000000000000..f9a7e7bc925b --- /dev/null +++ b/include/linux/usb/usb_phy_gen_xceiv.h @@ -0,0 +1,29 @@ +#ifndef __LINUX_USB_NOP_XCEIV_H +#define __LINUX_USB_NOP_XCEIV_H + +#include + +struct usb_phy_gen_xceiv_platform_data { + enum usb_phy_type type; + unsigned long clk_rate; + + /* if set fails with -EPROBE_DEFER if can't get regulator */ + unsigned int needs_vcc:1; + unsigned int needs_reset:1; +}; + +#if IS_ENABLED(CONFIG_NOP_USB_XCEIV) +/* sometimes transceivers are accessed only through e.g. ULPI */ +extern void usb_nop_xceiv_register(void); +extern void usb_nop_xceiv_unregister(void); +#else +static inline void usb_nop_xceiv_register(void) +{ +} + +static inline void usb_nop_xceiv_unregister(void) +{ +} +#endif + +#endif /* __LINUX_USB_NOP_XCEIV_H */ -- cgit v1.2.3-58-ga151 From 53b6fc28ea8e9857b6141afb92f3683eab9568ba Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 30 Jul 2013 17:20:06 +0200 Subject: usb: phy: phy-generic: export init functions This patch exports the mostly generic functions so they can be used from other phy driver instead of duplicating the code. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-generic.c | 132 +++++++++++++++++++++++------------------- drivers/usb/phy/phy-generic.h | 20 +++++++ 2 files changed, 94 insertions(+), 58 deletions(-) create mode 100644 drivers/usb/phy/phy-generic.h (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 406f8e43f852..efe59f3f7fda 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -36,13 +36,7 @@ #include #include -struct usb_phy_gen_xceiv { - struct usb_phy phy; - struct device *dev; - struct clk *clk; - struct regulator *vcc; - struct regulator *reset; -}; +#include "phy-generic.h" static struct platform_device *pd; @@ -70,7 +64,7 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) return 0; } -static int nop_init(struct usb_phy *phy) +int usb_gen_phy_init(struct usb_phy *phy) { struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); @@ -90,8 +84,9 @@ static int nop_init(struct usb_phy *phy) return 0; } +EXPORT_SYMBOL_GPL(usb_gen_phy_init); -static void nop_shutdown(struct usb_phy *phy) +void usb_gen_phy_shutdown(struct usb_phy *phy) { struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); @@ -109,6 +104,7 @@ static void nop_shutdown(struct usb_phy *phy) dev_err(phy->dev, "Failed to disable power\n"); } } +EXPORT_SYMBOL_GPL(usb_gen_phy_shutdown); static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) { @@ -139,53 +135,27 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } -static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) +int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, + enum usb_phy_type type, u32 clk_rate, bool needs_vcc, + bool needs_reset) { - struct device *dev = &pdev->dev; - struct usb_phy_gen_xceiv_platform_data *pdata = - dev_get_platdata(&pdev->dev); - struct usb_phy_gen_xceiv *nop; - enum usb_phy_type type = USB_PHY_TYPE_USB2; int err; - u32 clk_rate = 0; - bool needs_vcc = false; - bool needs_reset = false; - nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); - if (!nop) - return -ENOMEM; - - nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), - GFP_KERNEL); + nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg), + GFP_KERNEL); if (!nop->phy.otg) return -ENOMEM; - if (dev->of_node) { - struct device_node *node = dev->of_node; - - if (of_property_read_u32(node, "clock-frequency", &clk_rate)) - clk_rate = 0; - - needs_vcc = of_property_read_bool(node, "vcc-supply"); - needs_reset = of_property_read_bool(node, "reset-supply"); - - } else if (pdata) { - type = pdata->type; - clk_rate = pdata->clk_rate; - needs_vcc = pdata->needs_vcc; - needs_reset = pdata->needs_reset; - } - - nop->clk = devm_clk_get(&pdev->dev, "main_clk"); + nop->clk = devm_clk_get(dev, "main_clk"); if (IS_ERR(nop->clk)) { - dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", + dev_dbg(dev, "Can't get phy clock: %ld\n", PTR_ERR(nop->clk)); } if (!IS_ERR(nop->clk) && clk_rate) { err = clk_set_rate(nop->clk, clk_rate); if (err) { - dev_err(&pdev->dev, "Error setting clock rate\n"); + dev_err(dev, "Error setting clock rate\n"); return err; } } @@ -193,33 +163,31 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) if (!IS_ERR(nop->clk)) { err = clk_prepare(nop->clk); if (err) { - dev_err(&pdev->dev, "Error preparing clock\n"); + dev_err(dev, "Error preparing clock\n"); return err; } } - nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); + nop->vcc = devm_regulator_get(dev, "vcc"); if (IS_ERR(nop->vcc)) { - dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", + dev_dbg(dev, "Error getting vcc regulator: %ld\n", PTR_ERR(nop->vcc)); if (needs_vcc) return -EPROBE_DEFER; } - nop->reset = devm_regulator_get(&pdev->dev, "reset"); + nop->reset = devm_regulator_get(dev, "reset"); if (IS_ERR(nop->reset)) { - dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", + dev_dbg(dev, "Error getting reset regulator: %ld\n", PTR_ERR(nop->reset)); if (needs_reset) return -EPROBE_DEFER; } - nop->dev = &pdev->dev; + nop->dev = dev; nop->phy.dev = nop->dev; nop->phy.label = "nop-xceiv"; nop->phy.set_suspend = nop_set_suspend; - nop->phy.init = nop_init; - nop->phy.shutdown = nop_shutdown; nop->phy.state = OTG_STATE_UNDEFINED; nop->phy.type = type; @@ -227,6 +195,59 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; + ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); + return 0; +} +EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); + +void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop) +{ + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); +} +EXPORT_SYMBOL_GPL(usb_phy_gen_cleanup_phy); + +static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_phy_gen_xceiv_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct usb_phy_gen_xceiv *nop; + enum usb_phy_type type = USB_PHY_TYPE_USB2; + int err; + u32 clk_rate = 0; + bool needs_vcc = false; + bool needs_reset = false; + + if (dev->of_node) { + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node, "clock-frequency", &clk_rate)) + clk_rate = 0; + + needs_vcc = of_property_read_bool(node, "vcc-supply"); + needs_reset = of_property_read_bool(node, "reset-supply"); + + } else if (pdata) { + type = pdata->type; + clk_rate = pdata->clk_rate; + needs_vcc = pdata->needs_vcc; + needs_reset = pdata->needs_reset; + } + + nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL); + if (!nop) + return -ENOMEM; + + + err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc, + needs_reset); + if (err) + return err; + + nop->phy.init = usb_gen_phy_init; + nop->phy.shutdown = usb_gen_phy_shutdown; + err = usb_add_phy_dev(&nop->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", @@ -236,13 +257,10 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, nop); - ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); - return 0; err_add: - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); + usb_phy_gen_cleanup_phy(nop); return err; } @@ -250,9 +268,7 @@ static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) { struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - + usb_phy_gen_cleanup_phy(nop); usb_remove_phy(&nop->phy); return 0; diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h new file mode 100644 index 000000000000..61687d5a965b --- /dev/null +++ b/drivers/usb/phy/phy-generic.h @@ -0,0 +1,20 @@ +#ifndef _PHY_GENERIC_H_ +#define _PHY_GENERIC_H_ + +struct usb_phy_gen_xceiv { + struct usb_phy phy; + struct device *dev; + struct clk *clk; + struct regulator *vcc; + struct regulator *reset; +}; + +int usb_gen_phy_init(struct usb_phy *phy); +void usb_gen_phy_shutdown(struct usb_phy *phy); + +int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, + enum usb_phy_type type, u32 clk_rate, bool needs_vcc, + bool needs_reset); +void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop); + +#endif -- cgit v1.2.3-58-ga151 From 3bb869c8b3f1a11f1854cd74ebdeb60753614cf8 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 30 Jul 2013 21:43:45 +0200 Subject: usb: phy: Add AM335x PHY driver This driver is a redo of my earlier attempt. It uses parts of the generic PHY driver and uses the new control driver for the register the phy needs to power on/off the phy. It also enables easy access for the wakeup register which is not yet implemented. The difference between the omap attempt is: - no static holding variable - one global visible function which exports a struct with callbacks to access the "control" registers. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 12 ++++ drivers/usb/phy/Makefile | 2 + drivers/usb/phy/am35x-phy-control.h | 21 ++++++ drivers/usb/phy/phy-am335x-control.c | 136 +++++++++++++++++++++++++++++++++++ drivers/usb/phy/phy-am335x.c | 99 +++++++++++++++++++++++++ 5 files changed, 270 insertions(+) create mode 100644 drivers/usb/phy/am35x-phy-control.h create mode 100644 drivers/usb/phy/phy-am335x-control.c create mode 100644 drivers/usb/phy/phy-am335x.c (limited to 'drivers/usb') diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index f5ea339ec155..f41c3e12c6a7 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -86,6 +86,18 @@ config OMAP_USB3 This driver interacts with the "OMAP Control USB Driver" to power on/off the PHY. +config AM335X_CONTROL_USB + tristate + +config AM335X_PHY_USB + tristate "AM335x USB PHY Driver" + select USB_PHY + select AM335X_CONTROL_USB + select NOP_USB_XCEIV + help + This driver provides PHY support for that phy which part for the + AM335x SoC. + config SAMSUNG_USBPHY tristate help diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 24c5816409fd..dae321de3b2e 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -16,6 +16,8 @@ obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o +obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o +obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o diff --git a/drivers/usb/phy/am35x-phy-control.h b/drivers/usb/phy/am35x-phy-control.h new file mode 100644 index 000000000000..b96594d1962c --- /dev/null +++ b/drivers/usb/phy/am35x-phy-control.h @@ -0,0 +1,21 @@ +#ifndef _AM335x_PHY_CONTROL_H_ +#define _AM335x_PHY_CONTROL_H_ + +struct phy_control { + void (*phy_power)(struct phy_control *phy_ctrl, u32 id, bool on); + void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on); +}; + +static inline void phy_ctrl_power(struct phy_control *phy_ctrl, u32 id, bool on) +{ + phy_ctrl->phy_power(phy_ctrl, id, on); +} + +static inline void phy_ctrl_wkup(struct phy_control *phy_ctrl, u32 id, bool on) +{ + phy_ctrl->phy_wkup(phy_ctrl, id, on); +} + +struct phy_control *am335x_get_phy_control(struct device *dev); + +#endif diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c new file mode 100644 index 000000000000..35494f1c33b6 --- /dev/null +++ b/drivers/usb/phy/phy-am335x-control.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +struct phy_control { + void (*phy_power)(struct phy_control *phy_ctrl, u32 id, bool on); + void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on); +}; + +struct am335x_control_usb { + struct device *dev; + void __iomem *phy_reg; + void __iomem *wkup; + spinlock_t lock; + struct phy_control phy_ctrl; +}; + +#define AM335X_USB0_CTRL 0x0 +#define AM335X_USB1_CTRL 0x8 +#define AM335x_USB_WKUP 0x0 + +#define USBPHY_CM_PWRDN (1 << 0) +#define USBPHY_OTG_PWRDN (1 << 1) +#define USBPHY_OTGVDET_EN (1 << 19) +#define USBPHY_OTGSESSEND_EN (1 << 20) + +static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on) +{ + struct am335x_control_usb *usb_ctrl; + u32 val; + u32 reg; + + usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl); + + switch (id) { + case 0: + reg = AM335X_USB0_CTRL; + break; + case 1: + reg = AM335X_USB1_CTRL; + break; + default: + __WARN(); + return; + } + + val = readl(usb_ctrl->phy_reg + reg); + if (on) { + val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN); + val |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN; + } else { + val |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN; + } + + writel(val, usb_ctrl->phy_reg + reg); +} + +static const struct phy_control ctrl_am335x = { + .phy_power = am335x_phy_power, +}; + +static const struct of_device_id omap_control_usb_id_table[] = { + { .compatible = "ti,am335x-usb-ctrl-module", .data = &ctrl_am335x }, + {} +}; +MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); + +static struct platform_driver am335x_control_driver; +static int match(struct device *dev, void *data) +{ + struct device_node *node = (struct device_node *)data; + return dev->of_node == node && + dev->driver == &am335x_control_driver.driver; +} + +struct phy_control *am335x_get_phy_control(struct device *dev) +{ + struct device_node *node; + struct am335x_control_usb *ctrl_usb; + + node = of_parse_phandle(dev->of_node, "ti,ctrl_mod", 0); + if (!node) + return NULL; + + dev = bus_find_device(&platform_bus_type, NULL, node, match); + ctrl_usb = dev_get_drvdata(dev); + if (!ctrl_usb) + return NULL; + return &ctrl_usb->phy_ctrl; +} +EXPORT_SYMBOL_GPL(am335x_get_phy_control); + +static int am335x_control_usb_probe(struct platform_device *pdev) +{ + struct resource *res; + struct am335x_control_usb *ctrl_usb; + const struct of_device_id *of_id; + const struct phy_control *phy_ctrl; + + of_id = of_match_node(omap_control_usb_id_table, pdev->dev.of_node); + if (!of_id) + return -EINVAL; + + phy_ctrl = of_id->data; + + ctrl_usb = devm_kzalloc(&pdev->dev, sizeof(*ctrl_usb), GFP_KERNEL); + if (!ctrl_usb) { + dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); + return -ENOMEM; + } + + ctrl_usb->dev = &pdev->dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); + ctrl_usb->phy_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ctrl_usb->phy_reg)) + return PTR_ERR(ctrl_usb->phy_reg); + spin_lock_init(&ctrl_usb->lock); + ctrl_usb->phy_ctrl = *phy_ctrl; + + dev_set_drvdata(ctrl_usb->dev, ctrl_usb); + return 0; +} + +static struct platform_driver am335x_control_driver = { + .probe = am335x_control_usb_probe, + .driver = { + .name = "am335x-control-usb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(omap_control_usb_id_table), + }, +}; + +module_platform_driver(am335x_control_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c new file mode 100644 index 000000000000..c4d614d1f173 --- /dev/null +++ b/drivers/usb/phy/phy-am335x.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "am35x-phy-control.h" +#include "phy-generic.h" + +struct am335x_phy { + struct usb_phy_gen_xceiv usb_phy_gen; + struct phy_control *phy_ctrl; + int id; +}; + +static int am335x_init(struct usb_phy *phy) +{ + struct am335x_phy *am_phy = dev_get_drvdata(phy->dev); + + phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true); + return 0; +} + +static void am335x_shutdown(struct usb_phy *phy) +{ + struct am335x_phy *am_phy = dev_get_drvdata(phy->dev); + + phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false); +} + +static int am335x_phy_probe(struct platform_device *pdev) +{ + struct am335x_phy *am_phy; + struct device *dev = &pdev->dev; + int ret; + + am_phy = devm_kzalloc(dev, sizeof(*am_phy), GFP_KERNEL); + if (!am_phy) + return -ENOMEM; + + am_phy->phy_ctrl = am335x_get_phy_control(dev); + if (!am_phy->phy_ctrl) + return -EPROBE_DEFER; + am_phy->id = of_alias_get_id(pdev->dev.of_node, "phy"); + if (am_phy->id < 0) { + dev_err(&pdev->dev, "Missing PHY id: %d\n", am_phy->id); + return am_phy->id; + } + + ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, + USB_PHY_TYPE_USB2, 0, false, false); + if (ret) + return ret; + + ret = usb_add_phy_dev(&am_phy->usb_phy_gen.phy); + if (ret) + goto err_add; + am_phy->usb_phy_gen.phy.init = am335x_init; + am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown; + + platform_set_drvdata(pdev, am_phy); + return 0; + +err_add: + usb_phy_gen_cleanup_phy(&am_phy->usb_phy_gen); + return ret; +} + +static int am335x_phy_remove(struct platform_device *pdev) +{ + struct am335x_phy *am_phy = platform_get_drvdata(pdev); + + usb_remove_phy(&am_phy->usb_phy_gen.phy); + return 0; +} + +static const struct of_device_id am335x_phy_ids[] = { + { .compatible = "ti,am335x-usb-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, am335x_phy_ids); + +static struct platform_driver am335x_phy_driver = { + .probe = am335x_phy_probe, + .remove = am335x_phy_remove, + .driver = { + .name = "am335x-phy-driver", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(am335x_phy_ids), + }, +}; + +module_platform_driver(am335x_phy_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From e96bdc3dafe471375e2e780e319e3ead2d9ad4a7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 5 Jul 2013 12:33:33 +0200 Subject: usb: musb: dsps: remove the hardcoded phy pieces dsps uses a nop driver which is added in dsps itself and does the PHY on/off calls within dsps. Since those calls are now moved the nop driver itself, we can now request the phy proper phy and remove those calls. Currently only the first musb interface is used so we only add one phy node for now. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 97 +------------------------------------------- 1 file changed, 1 insertion(+), 96 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index e7a2cd1c6630..74333321cb62 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -120,49 +120,8 @@ struct dsps_glue { const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ struct timer_list timer[2]; /* otg_workaround timer */ unsigned long last_timer[2]; /* last timer data for each instance */ - u32 __iomem *usb_ctrl[2]; }; -#define DSPS_AM33XX_CONTROL_MODULE_PHYS_0 0x44e10620 -#define DSPS_AM33XX_CONTROL_MODULE_PHYS_1 0x44e10628 - -static const resource_size_t dsps_control_module_phys[] = { - DSPS_AM33XX_CONTROL_MODULE_PHYS_0, - DSPS_AM33XX_CONTROL_MODULE_PHYS_1, -}; - -#define USBPHY_CM_PWRDN (1 << 0) -#define USBPHY_OTG_PWRDN (1 << 1) -#define USBPHY_OTGVDET_EN (1 << 19) -#define USBPHY_OTGSESSEND_EN (1 << 20) - -/** - * musb_dsps_phy_control - phy on/off - * @glue: struct dsps_glue * - * @id: musb instance - * @on: flag for phy to be switched on or off - * - * This is to enable the PHY using usb_ctrl register in system control - * module space. - * - * XXX: This function will be removed once we have a seperate driver for - * control module - */ -static void musb_dsps_phy_control(struct dsps_glue *glue, u8 id, u8 on) -{ - u32 usbphycfg; - - usbphycfg = readl(glue->usb_ctrl[id]); - - if (on) { - usbphycfg &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN); - usbphycfg |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN; - } else { - usbphycfg |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN; - } - - writel(usbphycfg, glue->usb_ctrl[id]); -} /** * dsps_musb_enable - enable interrupts */ @@ -407,8 +366,7 @@ static int dsps_musb_init(struct musb *musb) musb->mregs += wrp->musb_core_offset; /* NOP driver needs change if supporting dual instance */ - usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); + musb->xceiv = devm_usb_get_phy_by_phandle(glue->dev, "phys", 0); if (IS_ERR_OR_NULL(musb->xceiv)) return -EPROBE_DEFER; @@ -426,9 +384,6 @@ static int dsps_musb_init(struct musb *musb) /* Reset the musb */ dsps_writel(reg_base, wrp->control, (1 << wrp->reset)); - /* Start the on-chip PHY and its PLL. */ - musb_dsps_phy_control(glue, pdev->id, 1); - musb->isr = dsps_interrupt; /* reset the otgdisable bit, needed for host mode to work */ @@ -438,8 +393,6 @@ static int dsps_musb_init(struct musb *musb) return 0; err0: - usb_put_phy(musb->xceiv); - usb_nop_xceiv_unregister(); return status; } @@ -451,14 +404,7 @@ static int dsps_musb_exit(struct musb *musb) del_timer_sync(&glue->timer[pdev->id]); - /* Shutdown the on-chip PHY and its PLL. */ - musb_dsps_phy_control(glue, pdev->id, 0); usb_phy_shutdown(musb->xceiv); - - /* NOP driver needs change if supporting dual instance */ - usb_put_phy(musb->xceiv); - usb_nop_xceiv_unregister(); - return 0; } @@ -487,16 +433,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, u8 id) char res_name[11]; int ret; - resources[0].start = dsps_control_module_phys[id]; - resources[0].end = resources[0].start + SZ_4 - 1; - resources[0].flags = IORESOURCE_MEM; - - glue->usb_ctrl[id] = devm_ioremap_resource(&pdev->dev, resources); - if (IS_ERR(glue->usb_ctrl[id])) { - ret = PTR_ERR(glue->usb_ctrl[id]); - goto err0; - } - /* first resource is for usbss, so start index from 1 */ res = platform_get_resource(pdev, IORESOURCE_MEM, id + 1); if (!res) { @@ -680,36 +616,6 @@ static int dsps_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int dsps_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev->parent); - struct dsps_glue *glue = platform_get_drvdata(pdev); - const struct dsps_musb_wrapper *wrp = glue->wrp; - int i; - - for (i = 0; i < wrp->instances; i++) - musb_dsps_phy_control(glue, i, 0); - - return 0; -} - -static int dsps_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev->parent); - struct dsps_glue *glue = platform_get_drvdata(pdev); - const struct dsps_musb_wrapper *wrp = glue->wrp; - int i; - - for (i = 0; i < wrp->instances; i++) - musb_dsps_phy_control(glue, i, 1); - - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume); - static const struct dsps_musb_wrapper am33xx_driver_data = { .revision = 0x00, .control = 0x14, @@ -752,7 +658,6 @@ static struct platform_driver dsps_usbss_driver = { .remove = dsps_remove, .driver = { .name = "musb-dsps", - .pm = &dsps_pm_ops, .of_match_table = of_match_ptr(musb_dsps_of_match), }, }; -- cgit v1.2.3-58-ga151 From 97238b35d5bbb5d5312d83c30a429824b777619f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 5 Jul 2013 14:51:33 +0200 Subject: usb: musb: dsps: use proper child nodes This moves the two instances from the big node into two child nodes. The glue layer ontop does almost nothing. There is one devices containing the control module for USB (2) phy, (2) usb and later the dma engine. The usb device is the "glue device" which contains the musb device as a child. This is what we do ever since. The new file musb_am335x is just here to prob the new bus and populate child devices. There are a lot of changes to the dsps file as a result of the changes: - musb_core_offset This is gone. The device tree provides memory ressources information for the device there is no need to "fix" things - instances This is gone as well. If we have two instances then we have have two child enabled nodes in the device tree. For instance the SoC in beagle bone has two USB instances but only one has been wired up so there is no need to load and init the second instance since it won't be used. - dsps_glue is now per glue device In the past there was one of this structs but with an array of two and each instance accessed its variable depending on the platform device id. - no unneeded copy of structs I do not know why struct dsps_musb_wrapper is copied but it is not necessary. The same goes for musb_hdrc_platform_data which allocated on demand and then again by platform_device_add_data(). One copy is enough. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am335x-bone.dts | 16 +++ arch/arm/boot/dts/am335x-evm.dts | 24 ++++ arch/arm/boot/dts/am335x-evmsk.dts | 16 +++ arch/arm/boot/dts/am33xx.dtsi | 97 ++++++++++++-- drivers/usb/musb/Kconfig | 4 + drivers/usb/musb/Makefile | 3 + drivers/usb/musb/musb_am335x.c | 55 ++++++++ drivers/usb/musb/musb_dsps.c | 255 ++++++++++++++----------------------- 8 files changed, 299 insertions(+), 171 deletions(-) create mode 100644 drivers/usb/musb/musb_am335x.c (limited to 'drivers/usb') diff --git a/arch/arm/boot/dts/am335x-bone.dts b/arch/arm/boot/dts/am335x-bone.dts index 444b4ede0d60..a8907b57c75c 100644 --- a/arch/arm/boot/dts/am335x-bone.dts +++ b/arch/arm/boot/dts/am335x-bone.dts @@ -120,6 +120,22 @@ status = "okay"; }; + musb: usb@47400000 { + status = "okay"; + + control@44e10000 { + status = "okay"; + }; + + phy@47401300 { + status = "okay"; + }; + + usb@47401000 { + status = "okay"; + }; + }; + i2c0: i2c@44e0b000 { pinctrl-names = "default"; pinctrl-0 = <&i2c0_pins>; diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index 3aee1a43782d..b2987e03b19f 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -171,6 +171,30 @@ }; }; + musb: usb@47400000 { + status = "okay"; + + control@44e10000 { + status = "okay"; + }; + + phy@47401300 { + status = "okay"; + }; + + phy@47401b00 { + status = "okay"; + }; + + usb@47401000 { + status = "okay"; + }; + + usb@47401800 { + status = "okay"; + }; + }; + i2c1: i2c@4802a000 { pinctrl-names = "default"; pinctrl-0 = <&i2c1_pins>; diff --git a/arch/arm/boot/dts/am335x-evmsk.dts b/arch/arm/boot/dts/am335x-evmsk.dts index 0c8ad173d2b0..e92446c6846e 100644 --- a/arch/arm/boot/dts/am335x-evmsk.dts +++ b/arch/arm/boot/dts/am335x-evmsk.dts @@ -207,6 +207,22 @@ }; }; + musb: usb@47400000 { + status = "okay"; + + control@44e10000 { + status = "okay"; + }; + + phy@47401300 { + status = "okay"; + }; + + usb@47401000 { + status = "okay"; + }; + }; + epwmss2: epwmss@48304000 { status = "okay"; diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 38b446ba1ce1..e1e773cd2556 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -26,6 +26,10 @@ serial5 = &uart5; d_can0 = &dcan0; d_can1 = &dcan1; + usb0 = &usb0; + usb1 = &usb1; + phy0 = &usb0_phy; + phy1 = &usb1_phy; }; cpus { @@ -333,21 +337,86 @@ status = "disabled"; }; - usb@47400000 { - compatible = "ti,musb-am33xx"; - reg = <0x47400000 0x1000 /* usbss */ - 0x47401000 0x800 /* musb instance 0 */ - 0x47401800 0x800>; /* musb instance 1 */ - interrupts = <17 /* usbss */ - 18 /* musb instance 0 */ - 19>; /* musb instance 1 */ - multipoint = <1>; - num-eps = <16>; - ram-bits = <12>; - port0-mode = <3>; - port1-mode = <3>; - power = <250>; + usb: usb@47400000 { + compatible = "ti,am33xx-usb"; + reg = <0x47400000 0x1000>; + ranges; + #address-cells = <1>; + #size-cells = <1>; ti,hwmods = "usb_otg_hs"; + status = "disabled"; + + ctrl_mod: control@44e10000 { + compatible = "ti,am335x-usb-ctrl-module"; + reg = <0x44e10620 0x10 + 0x44e10648 0x4>; + reg-names = "phy_ctrl", "wakeup"; + status = "disabled"; + }; + + usb0_phy: phy@47401300 { + compatible = "ti,am335x-usb-phy"; + reg = <0x47401300 0x100>; + reg-names = "phy"; + status = "disabled"; + ti,ctrl_mod = <&ctrl_mod>; + }; + + usb0: usb@47401000 { + compatible = "ti,musb-am33xx"; + ranges; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x47401000 0x200>; + reg-names = "control"; + status = "disabled"; + + musb0: usb@47401400 { + compatible = "mg,musbmhdrc"; + reg = <0x47401400 0x400>; + reg-names = "mc"; + interrupts = <18>; + interrupt-names = "mc"; + multipoint = <1>; + num-eps = <16>; + ram-bits = <12>; + port-mode = <3>; + power = <250>; + phys = <&usb0_phy>; + }; + }; + + usb1_phy: phy@47401b00 { + compatible = "ti,am335x-usb-phy"; + reg = <0x47401b00 0x100>; + reg-names = "phy"; + status = "disabled"; + ti,ctrl_mod = <&ctrl_mod>; + }; + + usb1: usb@47401800 { + compatible = "ti,musb-am33xx"; + ranges; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x47401800 0x200>; + reg-names = "control"; + status = "disabled"; + + musb1: usb@47401c00 { + compatible = "mg,musbmhdrc"; + reg = <0x47401c00 0x400>; + reg-names = "mc"; + interrupts = <19>; + interrupt-names = "mc"; + multipoint = <1>; + num-eps = <16>; + ram-bits = <12>; + port-mode = <3>; + power = <250>; + phys = <&usb1_phy>; + }; + }; }; epwmss0: epwmss@48300000 { diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 797e3fd45510..b7257ae038fd 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -83,6 +83,7 @@ config USB_MUSB_AM35X config USB_MUSB_DSPS tristate "TI DSPS platforms" + select USB_MUSB_AM335X_CHILD config USB_MUSB_BLACKFIN tristate "Blackfin" @@ -93,6 +94,9 @@ config USB_MUSB_UX500 endchoice +config USB_MUSB_AM335X_CHILD + tristate + choice prompt 'MUSB DMA mode' default MUSB_PIO_ONLY if ARCH_MULTIPLATFORM diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 2b82ed7c85ca..52f552c1ba2b 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -20,6 +20,9 @@ obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o obj-$(CONFIG_USB_MUSB_UX500) += ux500.o + +obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o + # the kconfig must guarantee that only one of the # possible I/O schemes will be enabled at a time ... # PIO only, or DMA (several potential schemes). diff --git a/drivers/usb/musb/musb_am335x.c b/drivers/usb/musb/musb_am335x.c new file mode 100644 index 000000000000..41ac5b5b57ce --- /dev/null +++ b/drivers/usb/musb/musb_am335x.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +static int am335x_child_probe(struct platform_device *pdev) +{ + int ret; + + pm_runtime_enable(&pdev->dev); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) + goto err; + + return 0; +err: + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int of_remove_populated_child(struct device *dev, void *d) +{ + struct platform_device *pdev = to_platform_device(dev); + + of_device_unregister(pdev); + return 0; +} + +static int am335x_child_remove(struct platform_device *pdev) +{ + device_for_each_child(&pdev->dev, NULL, of_remove_populated_child); + pm_runtime_disable(&pdev->dev); + return 0; +} + +static const struct of_device_id am335x_child_of_match[] = { + { .compatible = "ti,am33xx-usb" }, + { }, +}; +MODULE_DEVICE_TABLE(of, am335x_child_of_match); + +static struct platform_driver am335x_child_driver = { + .probe = am335x_child_probe, + .remove = am335x_child_remove, + .driver = { + .name = "am335x-usb-childs", + .of_match_table = of_match_ptr(am335x_child_of_match), + }, +}; + +module_platform_driver(am335x_child_driver); +MODULE_DESCRIPTION("AM33xx child devices"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 74333321cb62..4ffbaace7913 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "musb_core.h" @@ -105,10 +106,7 @@ struct dsps_musb_wrapper { /* bit positions for mode */ unsigned iddig:5; /* miscellaneous stuff */ - u32 musb_core_offset; u8 poll_seconds; - /* number of musb instances */ - u8 instances; }; /** @@ -116,10 +114,10 @@ struct dsps_musb_wrapper { */ struct dsps_glue { struct device *dev; - struct platform_device *musb[2]; /* child musb pdev */ + struct platform_device *musb; /* child musb pdev */ const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ - struct timer_list timer[2]; /* otg_workaround timer */ - unsigned long last_timer[2]; /* last timer data for each instance */ + struct timer_list timer; /* otg_workaround timer */ + unsigned long last_timer; /* last timer data for each instance */ }; /** @@ -168,7 +166,6 @@ static void otg_timer(unsigned long _musb) struct musb *musb = (void *)_musb; void __iomem *mregs = musb->mregs; struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; u8 devctl; @@ -205,7 +202,7 @@ static void otg_timer(unsigned long _musb) case OTG_STATE_B_IDLE: devctl = dsps_readb(mregs, MUSB_DEVCTL); if (devctl & MUSB_DEVCTL_BDEVICE) - mod_timer(&glue->timer[pdev->id], + mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); else musb->xceiv->state = OTG_STATE_A_IDLE; @@ -219,7 +216,6 @@ static void otg_timer(unsigned long _musb) static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) { struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); if (timeout == 0) @@ -230,23 +226,23 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", usb_otg_state_string(musb->xceiv->state)); - del_timer(&glue->timer[pdev->id]); - glue->last_timer[pdev->id] = jiffies; + del_timer(&glue->timer); + glue->last_timer = jiffies; return; } - if (time_after(glue->last_timer[pdev->id], timeout) && - timer_pending(&glue->timer[pdev->id])) { + if (time_after(glue->last_timer, timeout) && + timer_pending(&glue->timer)) { dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); return; } - glue->last_timer[pdev->id] = timeout; + glue->last_timer = timeout; dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", usb_otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); - mod_timer(&glue->timer[pdev->id], timeout); + mod_timer(&glue->timer, timeout); } static irqreturn_t dsps_interrupt(int irq, void *hci) @@ -254,7 +250,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) struct musb *musb = hci; void __iomem *reg_base = musb->ctrl_base; struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; unsigned long flags; @@ -314,7 +309,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) */ musb->int_usb &= ~MUSB_INTR_VBUSERROR; musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; - mod_timer(&glue->timer[pdev->id], + mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); WARNING("VBUS error workaround (delay coming)\n"); } else if (drvvbus) { @@ -322,7 +317,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) MUSB_HST_MODE(musb); musb->xceiv->otg->default_a = 1; musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; - del_timer(&glue->timer[pdev->id]); + del_timer(&glue->timer); } else { musb->is_active = 0; MUSB_DEV_MODE(musb); @@ -344,8 +339,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) /* Poll for ID change */ if (musb->xceiv->state == OTG_STATE_B_IDLE) - mod_timer(&glue->timer[pdev->id], - jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); out: spin_unlock_irqrestore(&musb->lock, flags); @@ -355,31 +349,34 @@ out: static int dsps_musb_init(struct musb *musb) { struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); + struct platform_device *parent = to_platform_device(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; - void __iomem *reg_base = musb->ctrl_base; + void __iomem *reg_base; + struct resource *r; u32 rev, val; - int status; - /* mentor core register starts at offset of 0x400 from musb base */ - musb->mregs += wrp->musb_core_offset; + r = platform_get_resource_byname(parent, IORESOURCE_MEM, "control"); + if (!r) + return -EINVAL; + + reg_base = devm_ioremap_resource(dev, r); + if (!musb->ctrl_base) + return -EINVAL; + musb->ctrl_base = reg_base; /* NOP driver needs change if supporting dual instance */ - musb->xceiv = devm_usb_get_phy_by_phandle(glue->dev, "phys", 0); - if (IS_ERR_OR_NULL(musb->xceiv)) - return -EPROBE_DEFER; + musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0); + if (IS_ERR(musb->xceiv)) + return PTR_ERR(musb->xceiv); /* Returns zero if e.g. not clocked */ rev = dsps_readl(reg_base, wrp->revision); - if (!rev) { - status = -ENODEV; - goto err0; - } + if (!rev) + return -ENODEV; usb_phy_init(musb->xceiv); - - setup_timer(&glue->timer[pdev->id], otg_timer, (unsigned long) musb); + setup_timer(&glue->timer, otg_timer, (unsigned long) musb); /* Reset the musb */ dsps_writel(reg_base, wrp->control, (1 << wrp->reset)); @@ -392,17 +389,14 @@ static int dsps_musb_init(struct musb *musb) dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); return 0; -err0: - return status; } static int dsps_musb_exit(struct musb *musb) { struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); - del_timer_sync(&glue->timer[pdev->id]); + del_timer_sync(&glue->timer); usb_phy_shutdown(musb->xceiv); return 0; @@ -420,106 +414,98 @@ static struct musb_platform_ops dsps_ops = { static u64 musb_dmamask = DMA_BIT_MASK(32); -static int dsps_create_musb_pdev(struct dsps_glue *glue, u8 id) +static int get_int_prop(struct device_node *dn, const char *s) { - struct device *dev = glue->dev; - struct platform_device *pdev = to_platform_device(dev); - struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); - struct device_node *np = pdev->dev.of_node; - struct musb_hdrc_config *config; - struct platform_device *musb; - struct resource *res; + int ret; + u32 val; + + ret = of_property_read_u32(dn, s, &val); + if (ret) + return 0; + return val; +} + +static int dsps_create_musb_pdev(struct dsps_glue *glue, + struct platform_device *parent) +{ + struct musb_hdrc_platform_data pdata; struct resource resources[2]; - char res_name[11]; + struct device *dev = &parent->dev; + struct musb_hdrc_config *config; + struct platform_device *musb; + struct device_node *dn = parent->dev.of_node; + struct device_node *child_node; int ret; - /* first resource is for usbss, so start index from 1 */ - res = platform_get_resource(pdev, IORESOURCE_MEM, id + 1); - if (!res) { - dev_err(dev, "failed to get memory for instance %d\n", id); - ret = -ENODEV; - goto err0; + child_node = of_get_child_by_name(dn, "usb"); + if (!child_node) + return -EINVAL; + + memset(resources, 0, sizeof(resources)); + ret = of_address_to_resource(child_node, 0, &resources[0]); + if (ret) { + dev_err(dev, "failed to get memory.\n"); + return ret; } - res->parent = NULL; - resources[0] = *res; - - /* first resource is for usbss, so start index from 1 */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, id + 1); - if (!res) { - dev_err(dev, "failed to get irq for instance %d\n", id); - ret = -ENODEV; - goto err0; + + ret = of_irq_to_resource(child_node, 0, &resources[1]); + if (ret == 0) { + dev_err(dev, "failed to get irq.\n"); + ret = -EINVAL; + return ret; } - res->parent = NULL; - resources[1] = *res; - resources[1].name = "mc"; /* allocate the child platform device */ musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); if (!musb) { dev_err(dev, "failed to allocate musb device\n"); - ret = -ENOMEM; - goto err0; + return -ENOMEM; } musb->dev.parent = dev; musb->dev.dma_mask = &musb_dmamask; musb->dev.coherent_dma_mask = musb_dmamask; + musb->dev.of_node = of_node_get(child_node); - glue->musb[id] = musb; + glue->musb = musb; - ret = platform_device_add_resources(musb, resources, 2); + ret = platform_device_add_resources(musb, resources, + ARRAY_SIZE(resources)); if (ret) { dev_err(dev, "failed to add resources\n"); - goto err2; + goto err; } - if (np) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(&pdev->dev, - "failed to allocate musb platform data\n"); - ret = -ENOMEM; - goto err2; - } - - config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); - if (!config) { - dev_err(&pdev->dev, - "failed to allocate musb hdrc config\n"); - ret = -ENOMEM; - goto err2; - } - - of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps); - of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits); - snprintf(res_name, sizeof(res_name), "port%d-mode", id); - of_property_read_u32(np, res_name, (u32 *)&pdata->mode); - of_property_read_u32(np, "power", (u32 *)&pdata->power); - config->multipoint = of_property_read_bool(np, "multipoint"); - - pdata->config = config; + config = devm_kzalloc(&parent->dev, sizeof(*config), GFP_KERNEL); + if (!config) { + dev_err(dev, "failed to allocate musb hdrc config\n"); + ret = -ENOMEM; + goto err; } + pdata.config = config; + pdata.platform_ops = &dsps_ops; - pdata->platform_ops = &dsps_ops; + config->num_eps = get_int_prop(child_node, "num-eps"); + config->ram_bits = get_int_prop(child_node, "ram-bits"); + pdata.mode = get_int_prop(child_node, "port-mode"); + pdata.power = get_int_prop(child_node, "power"); + config->multipoint = of_property_read_bool(child_node, "multipoint"); - ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + ret = platform_device_add_data(musb, &pdata, sizeof(pdata)); if (ret) { dev_err(dev, "failed to add platform_data\n"); - goto err2; + goto err; } ret = platform_device_add(musb); if (ret) { dev_err(dev, "failed to register musb device\n"); - goto err2; + goto err; } - return 0; -err2: +err: platform_device_put(musb); -err0: return ret; } @@ -528,14 +514,12 @@ static int dsps_probe(struct platform_device *pdev) const struct of_device_id *match; const struct dsps_musb_wrapper *wrp; struct dsps_glue *glue; - struct resource *iomem; - int ret, i; + int ret; match = of_match_node(musb_dsps_of_match, pdev->dev.of_node); if (!match) { dev_err(&pdev->dev, "fail to get matching of_match struct\n"); - ret = -EINVAL; - goto err0; + return -EINVAL; } wrp = match->data; @@ -543,29 +527,13 @@ static int dsps_probe(struct platform_device *pdev) glue = kzalloc(sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "unable to allocate glue memory\n"); - ret = -ENOMEM; - goto err0; - } - - /* get memory resource */ - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iomem) { - dev_err(&pdev->dev, "failed to get usbss mem resourse\n"); - ret = -ENODEV; - goto err1; + return -ENOMEM; } glue->dev = &pdev->dev; + glue->wrp = wrp; - glue->wrp = kmemdup(wrp, sizeof(*wrp), GFP_KERNEL); - if (!glue->wrp) { - dev_err(&pdev->dev, "failed to duplicate wrapper struct memory\n"); - ret = -ENOMEM; - goto err1; - } platform_set_drvdata(pdev, glue); - - /* enable the usbss clocks */ pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); @@ -574,17 +542,9 @@ static int dsps_probe(struct platform_device *pdev) goto err2; } - /* create the child platform device for all instances of musb */ - for (i = 0; i < wrp->instances ; i++) { - ret = dsps_create_musb_pdev(glue, i); - if (ret != 0) { - dev_err(&pdev->dev, "failed to create child pdev\n"); - /* release resources of previously created instances */ - for (i--; i >= 0 ; i--) - platform_device_unregister(glue->musb[i]); - goto err3; - } - } + ret = dsps_create_musb_pdev(glue, pdev); + if (ret) + goto err3; return 0; @@ -592,26 +552,19 @@ err3: pm_runtime_put(&pdev->dev); err2: pm_runtime_disable(&pdev->dev); - kfree(glue->wrp); -err1: kfree(glue); -err0: return ret; } + static int dsps_remove(struct platform_device *pdev) { struct dsps_glue *glue = platform_get_drvdata(pdev); - const struct dsps_musb_wrapper *wrp = glue->wrp; - int i; - /* delete the child platform device */ - for (i = 0; i < wrp->instances ; i++) - platform_device_unregister(glue->musb[i]); + platform_device_unregister(glue->musb); /* disable usbss clocks */ pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - kfree(glue->wrp); kfree(glue); return 0; } @@ -641,9 +594,7 @@ static const struct dsps_musb_wrapper am33xx_driver_data = { .rxep_shift = 16, .rxep_mask = 0xfffe, .rxep_bitmap = (0xfffe << 16), - .musb_core_offset = 0x400, .poll_seconds = 2, - .instances = 1, }; static const struct of_device_id musb_dsps_of_match[] = { @@ -667,14 +618,4 @@ MODULE_AUTHOR("Ravi B "); MODULE_AUTHOR("Ajay Kumar Gupta "); MODULE_LICENSE("GPL v2"); -static int __init dsps_init(void) -{ - return platform_driver_register(&dsps_usbss_driver); -} -subsys_initcall(dsps_init); - -static void __exit dsps_exit(void) -{ - platform_driver_unregister(&dsps_usbss_driver); -} -module_exit(dsps_exit); +module_platform_driver(dsps_usbss_driver); -- cgit v1.2.3-58-ga151 From 9b3452d1fa3c017d3664ff9e6a601daa6e0576eb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 20 Jun 2013 12:13:04 +0200 Subject: usb: musb dma: add cppi41 dma driver This driver is currently used by musb' cppi41 couter part. I may merge both dma engine user of musb at some point but not just yet. The driver seems to work in RX/TX mode in host mode, tested on mass storage. I increaed the size of the TX / RX transfers and waited for the core code to cancel a transfers and it seems to recover. v2..3: - use mall transfers on RX side and check data toggle. - use rndis mode on tx side so we haveon interrupt for 4096 transfers. - remove custom "transferred" hack and use dmaengine_tx_status() to compute the total amount of data that has been transferred. - cancel transfers and reclaim descriptors v1..v2: - RX path added - dma mode 0 & 1 is working - device tree nodes re-created. Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am335x-evm.dts | 4 + arch/arm/boot/dts/am33xx.dtsi | 61 +++ drivers/dma/Kconfig | 8 + drivers/dma/Makefile | 1 + drivers/dma/cppi41.c | 1048 ++++++++++++++++++++++++++++++++++++++ drivers/usb/musb/Kconfig | 4 + drivers/usb/musb/Makefile | 1 + drivers/usb/musb/musb_cppi41.c | 552 ++++++++++++++++++++ drivers/usb/musb/musb_dma.h | 2 +- 9 files changed, 1680 insertions(+), 1 deletion(-) create mode 100644 drivers/dma/cppi41.c create mode 100644 drivers/usb/musb/musb_cppi41.c (limited to 'drivers/usb') diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index b2987e03b19f..c26c16cace3c 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -193,6 +193,10 @@ usb@47401800 { status = "okay"; }; + + dma@07402000 { + status = "okay"; + }; }; i2c1: i2c@4802a000 { diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index e1e773cd2556..24d63095ab83 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -383,6 +383,29 @@ port-mode = <3>; power = <250>; phys = <&usb0_phy>; + + dmas = <&cppi41dma 0 0 &cppi41dma 1 0 + &cppi41dma 2 0 &cppi41dma 3 0 + &cppi41dma 4 0 &cppi41dma 5 0 + &cppi41dma 6 0 &cppi41dma 7 0 + &cppi41dma 8 0 &cppi41dma 9 0 + &cppi41dma 10 0 &cppi41dma 11 0 + &cppi41dma 12 0 &cppi41dma 13 0 + &cppi41dma 14 0 &cppi41dma 0 1 + &cppi41dma 1 1 &cppi41dma 2 1 + &cppi41dma 3 1 &cppi41dma 4 1 + &cppi41dma 5 1 &cppi41dma 6 1 + &cppi41dma 7 1 &cppi41dma 8 1 + &cppi41dma 9 1 &cppi41dma 10 1 + &cppi41dma 11 1 &cppi41dma 12 1 + &cppi41dma 13 1 &cppi41dma 14 1>; + dma-names = + "rx1", "rx2", "rx3", "rx4", "rx5", "rx6", "rx7", + "rx8", "rx9", "rx10", "rx11", "rx12", "rx13", + "rx14", "rx15", + "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", + "tx8", "tx9", "tx10", "tx11", "tx12", "tx13", + "tx14", "tx15"; }; }; @@ -415,8 +438,46 @@ port-mode = <3>; power = <250>; phys = <&usb1_phy>; + + dmas = <&cppi41dma 15 0 &cppi41dma 16 0 + &cppi41dma 17 0 &cppi41dma 18 0 + &cppi41dma 19 0 &cppi41dma 20 0 + &cppi41dma 21 0 &cppi41dma 22 0 + &cppi41dma 23 0 &cppi41dma 24 0 + &cppi41dma 25 0 &cppi41dma 26 0 + &cppi41dma 27 0 &cppi41dma 28 0 + &cppi41dma 29 0 &cppi41dma 15 1 + &cppi41dma 16 1 &cppi41dma 17 1 + &cppi41dma 18 1 &cppi41dma 19 1 + &cppi41dma 20 1 &cppi41dma 21 1 + &cppi41dma 22 1 &cppi41dma 23 1 + &cppi41dma 24 1 &cppi41dma 25 1 + &cppi41dma 26 1 &cppi41dma 27 1 + &cppi41dma 28 1 &cppi41dma 29 1>; + dma-names = + "rx1", "rx2", "rx3", "rx4", "rx5", "rx6", "rx7", + "rx8", "rx9", "rx10", "rx11", "rx12", "rx13", + "rx14", "rx15", + "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", + "tx8", "tx9", "tx10", "tx11", "tx12", "tx13", + "tx14", "tx15"; }; }; + + cppi41dma: dma@07402000 { + compatible = "ti,am3359-cppi41"; + reg = <0x47400000 0x1000 + 0x47402000 0x1000 + 0x47403000 0x1000 + 0x47404000 0x4000>; + reg-names = "glue controller scheduler queuemgr"; + interrupts = <17>; + interrupt-names = "glue"; + #dma-cells = <2>; + #dma-channels = <30>; + #dma-requests = <256>; + status = "disabled"; + }; }; epwmss0: epwmss@48300000 { diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 6825957c97fb..77bc480117b7 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -287,6 +287,14 @@ config DMA_OMAP select DMA_ENGINE select DMA_VIRTUAL_CHANNELS +config TI_CPPI41 + tristate "AM33xx CPPI41 DMA support" + depends on ARCH_OMAP + select DMA_ENGINE + help + The Communications Port Programming Interface (CPPI) 4.1 DMA engine + is currently used by the USB driver on AM335x platforms. + config MMP_PDMA bool "MMP PDMA support" depends on (ARCH_MMP || ARCH_PXA) diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 5e0f2ef85614..6d62ec30c4bc 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o obj-$(CONFIG_DMA_OMAP) += omap-dma.o obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o +obj-$(CONFIG_TI_CPPI41) += cppi41.o diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c new file mode 100644 index 000000000000..5dcebca37760 --- /dev/null +++ b/drivers/dma/cppi41.c @@ -0,0 +1,1048 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dmaengine.h" + +#define DESC_TYPE 27 +#define DESC_TYPE_HOST 0x10 +#define DESC_TYPE_TEARD 0x13 + +#define TD_DESC_IS_RX (1 << 16) +#define TD_DESC_DMA_NUM 10 + +#define DESC_LENGTH_BITS_NUM 21 + +#define DESC_TYPE_USB (5 << 26) +#define DESC_PD_COMPLETE (1 << 31) + +/* DMA engine */ +#define DMA_TDFDQ 4 +#define DMA_TXGCR(x) (0x800 + (x) * 0x20) +#define DMA_RXGCR(x) (0x808 + (x) * 0x20) +#define RXHPCRA0 4 + +#define GCR_CHAN_ENABLE (1 << 31) +#define GCR_TEARDOWN (1 << 30) +#define GCR_STARV_RETRY (1 << 24) +#define GCR_DESC_TYPE_HOST (1 << 14) + +/* DMA scheduler */ +#define DMA_SCHED_CTRL 0 +#define DMA_SCHED_CTRL_EN (1 << 31) +#define DMA_SCHED_WORD(x) ((x) * 4 + 0x800) + +#define SCHED_ENTRY0_CHAN(x) ((x) << 0) +#define SCHED_ENTRY0_IS_RX (1 << 7) + +#define SCHED_ENTRY1_CHAN(x) ((x) << 8) +#define SCHED_ENTRY1_IS_RX (1 << 15) + +#define SCHED_ENTRY2_CHAN(x) ((x) << 16) +#define SCHED_ENTRY2_IS_RX (1 << 23) + +#define SCHED_ENTRY3_CHAN(x) ((x) << 24) +#define SCHED_ENTRY3_IS_RX (1 << 31) + +/* Queue manager */ +/* 4 KiB of memory for descriptors, 2 for each endpoint */ +#define ALLOC_DECS_NUM 128 +#define DESCS_AREAS 1 +#define TOTAL_DESCS_NUM (ALLOC_DECS_NUM * DESCS_AREAS) +#define QMGR_SCRATCH_SIZE (TOTAL_DESCS_NUM * 4) + +#define QMGR_LRAM0_BASE 0x80 +#define QMGR_LRAM_SIZE 0x84 +#define QMGR_LRAM1_BASE 0x88 +#define QMGR_MEMBASE(x) (0x1000 + (x) * 0x10) +#define QMGR_MEMCTRL(x) (0x1004 + (x) * 0x10) +#define QMGR_MEMCTRL_IDX_SH 16 +#define QMGR_MEMCTRL_DESC_SH 8 + +#define QMGR_NUM_PEND 5 +#define QMGR_PEND(x) (0x90 + (x) * 4) + +#define QMGR_PENDING_SLOT_Q(x) (x / 32) +#define QMGR_PENDING_BIT_Q(x) (x % 32) + +#define QMGR_QUEUE_A(n) (0x2000 + (n) * 0x10) +#define QMGR_QUEUE_B(n) (0x2004 + (n) * 0x10) +#define QMGR_QUEUE_C(n) (0x2008 + (n) * 0x10) +#define QMGR_QUEUE_D(n) (0x200c + (n) * 0x10) + +/* Glue layer specific */ +/* USBSS / USB AM335x */ +#define USBSS_IRQ_STATUS 0x28 +#define USBSS_IRQ_ENABLER 0x2c +#define USBSS_IRQ_CLEARR 0x30 + +#define USBSS_IRQ_PD_COMP (1 << 2) + +struct cppi41_channel { + struct dma_chan chan; + struct dma_async_tx_descriptor txd; + struct cppi41_dd *cdd; + struct cppi41_desc *desc; + dma_addr_t desc_phys; + void __iomem *gcr_reg; + int is_tx; + u32 residue; + + unsigned int q_num; + unsigned int q_comp_num; + unsigned int port_num; + + unsigned td_retry; + unsigned td_queued:1; + unsigned td_seen:1; + unsigned td_desc_seen:1; +}; + +struct cppi41_desc { + u32 pd0; + u32 pd1; + u32 pd2; + u32 pd3; + u32 pd4; + u32 pd5; + u32 pd6; + u32 pd7; +} __aligned(32); + +struct chan_queues { + u16 submit; + u16 complete; +}; + +struct cppi41_dd { + struct dma_device ddev; + + void *qmgr_scratch; + dma_addr_t scratch_phys; + + struct cppi41_desc *cd; + dma_addr_t descs_phys; + u32 first_td_desc; + struct cppi41_channel *chan_busy[ALLOC_DECS_NUM]; + + void __iomem *usbss_mem; + void __iomem *ctrl_mem; + void __iomem *sched_mem; + void __iomem *qmgr_mem; + unsigned int irq; + const struct chan_queues *queues_rx; + const struct chan_queues *queues_tx; + struct chan_queues td_queue; +}; + +#define FIST_COMPLETION_QUEUE 93 +static struct chan_queues usb_queues_tx[] = { + /* USB0 ENDP 1 */ + [ 0] = { .submit = 32, .complete = 93}, + [ 1] = { .submit = 34, .complete = 94}, + [ 2] = { .submit = 36, .complete = 95}, + [ 3] = { .submit = 38, .complete = 96}, + [ 4] = { .submit = 40, .complete = 97}, + [ 5] = { .submit = 42, .complete = 98}, + [ 6] = { .submit = 44, .complete = 99}, + [ 7] = { .submit = 46, .complete = 100}, + [ 8] = { .submit = 48, .complete = 101}, + [ 9] = { .submit = 50, .complete = 102}, + [10] = { .submit = 52, .complete = 103}, + [11] = { .submit = 54, .complete = 104}, + [12] = { .submit = 56, .complete = 105}, + [13] = { .submit = 58, .complete = 106}, + [14] = { .submit = 60, .complete = 107}, + + /* USB1 ENDP1 */ + [15] = { .submit = 62, .complete = 125}, + [16] = { .submit = 64, .complete = 126}, + [17] = { .submit = 66, .complete = 127}, + [18] = { .submit = 68, .complete = 128}, + [19] = { .submit = 70, .complete = 129}, + [20] = { .submit = 72, .complete = 130}, + [21] = { .submit = 74, .complete = 131}, + [22] = { .submit = 76, .complete = 132}, + [23] = { .submit = 78, .complete = 133}, + [24] = { .submit = 80, .complete = 134}, + [25] = { .submit = 82, .complete = 135}, + [26] = { .submit = 84, .complete = 136}, + [27] = { .submit = 86, .complete = 137}, + [28] = { .submit = 88, .complete = 138}, + [29] = { .submit = 90, .complete = 139}, +}; + +static const struct chan_queues usb_queues_rx[] = { + /* USB0 ENDP 1 */ + [ 0] = { .submit = 1, .complete = 109}, + [ 1] = { .submit = 2, .complete = 110}, + [ 2] = { .submit = 3, .complete = 111}, + [ 3] = { .submit = 4, .complete = 112}, + [ 4] = { .submit = 5, .complete = 113}, + [ 5] = { .submit = 6, .complete = 114}, + [ 6] = { .submit = 7, .complete = 115}, + [ 7] = { .submit = 8, .complete = 116}, + [ 8] = { .submit = 9, .complete = 117}, + [ 9] = { .submit = 10, .complete = 118}, + [10] = { .submit = 11, .complete = 119}, + [11] = { .submit = 12, .complete = 120}, + [12] = { .submit = 13, .complete = 121}, + [13] = { .submit = 14, .complete = 122}, + [14] = { .submit = 15, .complete = 123}, + + /* USB1 ENDP 1 */ + [15] = { .submit = 16, .complete = 141}, + [16] = { .submit = 17, .complete = 142}, + [17] = { .submit = 18, .complete = 143}, + [18] = { .submit = 19, .complete = 144}, + [19] = { .submit = 20, .complete = 145}, + [20] = { .submit = 21, .complete = 146}, + [21] = { .submit = 22, .complete = 147}, + [22] = { .submit = 23, .complete = 148}, + [23] = { .submit = 24, .complete = 149}, + [24] = { .submit = 25, .complete = 150}, + [25] = { .submit = 26, .complete = 151}, + [26] = { .submit = 27, .complete = 152}, + [27] = { .submit = 28, .complete = 153}, + [28] = { .submit = 29, .complete = 154}, + [29] = { .submit = 30, .complete = 155}, +}; + +struct cppi_glue_infos { + irqreturn_t (*isr)(int irq, void *data); + const struct chan_queues *queues_rx; + const struct chan_queues *queues_tx; + struct chan_queues td_queue; +}; + +static struct cppi41_channel *to_cpp41_chan(struct dma_chan *c) +{ + return container_of(c, struct cppi41_channel, chan); +} + +static struct cppi41_channel *desc_to_chan(struct cppi41_dd *cdd, u32 desc) +{ + struct cppi41_channel *c; + u32 descs_size; + u32 desc_num; + + descs_size = sizeof(struct cppi41_desc) * ALLOC_DECS_NUM; + + if (!((desc >= cdd->descs_phys) && + (desc < (cdd->descs_phys + descs_size)))) { + return NULL; + } + + desc_num = (desc - cdd->descs_phys) / sizeof(struct cppi41_desc); + BUG_ON(desc_num > ALLOC_DECS_NUM); + c = cdd->chan_busy[desc_num]; + cdd->chan_busy[desc_num] = NULL; + return c; +} + +static void cppi_writel(u32 val, void *__iomem *mem) +{ + __raw_writel(val, mem); +} + +static u32 cppi_readl(void *__iomem *mem) +{ + return __raw_readl(mem); +} + +static u32 pd_trans_len(u32 val) +{ + return val & ((1 << (DESC_LENGTH_BITS_NUM + 1)) - 1); +} + +static irqreturn_t cppi41_irq(int irq, void *data) +{ + struct cppi41_dd *cdd = data; + struct cppi41_channel *c; + u32 status; + int i; + + status = cppi_readl(cdd->usbss_mem + USBSS_IRQ_STATUS); + if (!(status & USBSS_IRQ_PD_COMP)) + return IRQ_NONE; + cppi_writel(status, cdd->usbss_mem + USBSS_IRQ_STATUS); + + for (i = QMGR_PENDING_SLOT_Q(FIST_COMPLETION_QUEUE); i < QMGR_NUM_PEND; + i++) { + u32 val; + u32 q_num; + + val = cppi_readl(cdd->qmgr_mem + QMGR_PEND(i)); + if (i == QMGR_PENDING_SLOT_Q(FIST_COMPLETION_QUEUE) && val) { + u32 mask; + /* set corresponding bit for completetion Q 93 */ + mask = 1 << QMGR_PENDING_BIT_Q(FIST_COMPLETION_QUEUE); + /* not set all bits for queues less than Q 93 */ + mask--; + /* now invert and keep only Q 93+ set */ + val &= ~mask; + } + + if (val) + __iormb(); + + while (val) { + u32 desc; + + q_num = __fls(val); + val &= ~(1 << q_num); + q_num += 32 * i; + desc = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(q_num)); + desc &= ~0x1f; + c = desc_to_chan(cdd, desc); + if (WARN_ON(!c)) { + pr_err("%s() q %d desc %08x\n", __func__, + q_num, desc); + continue; + } + c->residue = pd_trans_len(c->desc->pd6) - + pd_trans_len(c->desc->pd0); + + dma_cookie_complete(&c->txd); + c->txd.callback(c->txd.callback_param); + } + } + return IRQ_HANDLED; +} + +static dma_cookie_t cppi41_tx_submit(struct dma_async_tx_descriptor *tx) +{ + dma_cookie_t cookie; + + cookie = dma_cookie_assign(tx); + + return cookie; +} + +static int cppi41_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + + dma_cookie_init(chan); + dma_async_tx_descriptor_init(&c->txd, chan); + c->txd.tx_submit = cppi41_tx_submit; + + if (!c->is_tx) + cppi_writel(c->q_num, c->gcr_reg + RXHPCRA0); + + return 0; +} + +static void cppi41_dma_free_chan_resources(struct dma_chan *chan) +{ +} + +static enum dma_status cppi41_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + enum dma_status ret; + + /* lock */ + ret = dma_cookie_status(chan, cookie, txstate); + if (txstate && ret == DMA_SUCCESS) + txstate->residue = c->residue; + /* unlock */ + + return ret; +} + +static void push_desc_queue(struct cppi41_channel *c) +{ + struct cppi41_dd *cdd = c->cdd; + u32 desc_num; + u32 desc_phys; + u32 reg; + + desc_phys = lower_32_bits(c->desc_phys); + desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc); + WARN_ON(cdd->chan_busy[desc_num]); + cdd->chan_busy[desc_num] = c; + + reg = (sizeof(struct cppi41_desc) - 24) / 4; + reg |= desc_phys; + cppi_writel(reg, cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num)); +} + +static void cppi41_dma_issue_pending(struct dma_chan *chan) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + u32 reg; + + c->residue = 0; + + reg = GCR_CHAN_ENABLE; + if (!c->is_tx) { + reg |= GCR_STARV_RETRY; + reg |= GCR_DESC_TYPE_HOST; + reg |= c->q_comp_num; + } + + cppi_writel(reg, c->gcr_reg); + + /* + * We don't use writel() but __raw_writel() so we have to make sure + * that the DMA descriptor in coherent memory made to the main memory + * before starting the dma engine. + */ + __iowmb(); + push_desc_queue(c); +} + +static u32 get_host_pd0(u32 length) +{ + u32 reg; + + reg = DESC_TYPE_HOST << DESC_TYPE; + reg |= length; + + return reg; +} + +static u32 get_host_pd1(struct cppi41_channel *c) +{ + u32 reg; + + reg = 0; + + return reg; +} + +static u32 get_host_pd2(struct cppi41_channel *c) +{ + u32 reg; + + reg = DESC_TYPE_USB; + reg |= c->q_comp_num; + + return reg; +} + +static u32 get_host_pd3(u32 length) +{ + u32 reg; + + /* PD3 = packet size */ + reg = length; + + return reg; +} + +static u32 get_host_pd6(u32 length) +{ + u32 reg; + + /* PD6 buffer size */ + reg = DESC_PD_COMPLETE; + reg |= length; + + return reg; +} + +static u32 get_host_pd4_or_7(u32 addr) +{ + u32 reg; + + reg = addr; + + return reg; +} + +static u32 get_host_pd5(void) +{ + u32 reg; + + reg = 0; + + return reg; +} + +static struct dma_async_tx_descriptor *cppi41_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned sg_len, + enum dma_transfer_direction dir, unsigned long tx_flags, void *context) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + struct cppi41_desc *d; + struct scatterlist *sg; + unsigned int i; + unsigned int num; + + num = 0; + d = c->desc; + for_each_sg(sgl, sg, sg_len, i) { + u32 addr; + u32 len; + + /* We need to use more than one desc once musb supports sg */ + BUG_ON(num > 0); + addr = lower_32_bits(sg_dma_address(sg)); + len = sg_dma_len(sg); + + d->pd0 = get_host_pd0(len); + d->pd1 = get_host_pd1(c); + d->pd2 = get_host_pd2(c); + d->pd3 = get_host_pd3(len); + d->pd4 = get_host_pd4_or_7(addr); + d->pd5 = get_host_pd5(); + d->pd6 = get_host_pd6(len); + d->pd7 = get_host_pd4_or_7(addr); + + d++; + } + + return &c->txd; +} + +static int cpp41_cfg_chan(struct cppi41_channel *c, + struct dma_slave_config *cfg) +{ + return 0; +} + +static void cppi41_compute_td_desc(struct cppi41_desc *d) +{ + d->pd0 = DESC_TYPE_TEARD << DESC_TYPE; +} + +static u32 cppi41_pop_desc(struct cppi41_dd *cdd, unsigned queue_num) +{ + u32 desc; + + desc = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(queue_num)); + desc &= ~0x1f; + return desc; +} + +static int cppi41_tear_down_chan(struct cppi41_channel *c) +{ + struct cppi41_dd *cdd = c->cdd; + struct cppi41_desc *td; + u32 reg; + u32 desc_phys; + u32 td_desc_phys; + + td = cdd->cd; + td += cdd->first_td_desc; + + td_desc_phys = cdd->descs_phys; + td_desc_phys += cdd->first_td_desc * sizeof(struct cppi41_desc); + + if (!c->td_queued) { + cppi41_compute_td_desc(td); + __iowmb(); + + reg = (sizeof(struct cppi41_desc) - 24) / 4; + reg |= td_desc_phys; + cppi_writel(reg, cdd->qmgr_mem + + QMGR_QUEUE_D(cdd->td_queue.submit)); + + reg = GCR_CHAN_ENABLE; + if (!c->is_tx) { + reg |= GCR_STARV_RETRY; + reg |= GCR_DESC_TYPE_HOST; + reg |= c->q_comp_num; + } + reg |= GCR_TEARDOWN; + cppi_writel(reg, c->gcr_reg); + c->td_queued = 1; + c->td_retry = 100; + } + + if (!c->td_seen) { + unsigned td_comp_queue; + + if (c->is_tx) + td_comp_queue = cdd->td_queue.complete; + else + td_comp_queue = c->q_comp_num; + + desc_phys = cppi41_pop_desc(cdd, td_comp_queue); + if (desc_phys) { + __iormb(); + + if (desc_phys == td_desc_phys) { + u32 pd0; + pd0 = td->pd0; + WARN_ON((pd0 >> DESC_TYPE) != DESC_TYPE_TEARD); + WARN_ON(!c->is_tx && !(pd0 & TD_DESC_IS_RX)); + WARN_ON((pd0 & 0x1f) != c->port_num); + } else { + __WARN(); + } + c->td_seen = 1; + } + } + if (!c->td_desc_seen) { + desc_phys = cppi41_pop_desc(cdd, c->q_comp_num); + if (desc_phys) { + __iormb(); + WARN_ON(c->desc_phys != desc_phys); + c->td_desc_seen = 1; + } + } + c->td_retry--; + /* + * If the TX descriptor / channel is in use, the caller needs to poke + * his TD bit multiple times. After that he hardware releases the + * transfer descriptor followed by TD descriptor. Waiting seems not to + * cause any difference. + * RX seems to be thrown out right away. However once the TearDown + * descriptor gets through we are done. If we have seens the transfer + * descriptor before the TD we fetch it from enqueue, it has to be + * there waiting for us. + */ + if (!c->td_seen && c->td_retry) + return -EAGAIN; + + WARN_ON(!c->td_retry); + if (!c->td_desc_seen) { + desc_phys = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num)); + WARN_ON(!desc_phys); + } + + c->td_queued = 0; + c->td_seen = 0; + c->td_desc_seen = 0; + cppi_writel(0, c->gcr_reg); + return 0; +} + +static int cppi41_stop_chan(struct dma_chan *chan) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + struct cppi41_dd *cdd = c->cdd; + u32 desc_num; + u32 desc_phys; + int ret; + + ret = cppi41_tear_down_chan(c); + if (ret) + return ret; + + desc_phys = lower_32_bits(c->desc_phys); + desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc); + WARN_ON(!cdd->chan_busy[desc_num]); + cdd->chan_busy[desc_num] = NULL; + + return 0; +} + +static int cppi41_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + int ret; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + ret = cpp41_cfg_chan(c, (struct dma_slave_config *) arg); + break; + + case DMA_TERMINATE_ALL: + ret = cppi41_stop_chan(chan); + break; + + default: + ret = -ENXIO; + break; + } + return ret; +} + +static void cleanup_chans(struct cppi41_dd *cdd) +{ + while (!list_empty(&cdd->ddev.channels)) { + struct cppi41_channel *cchan; + + cchan = list_first_entry(&cdd->ddev.channels, + struct cppi41_channel, chan.device_node); + list_del(&cchan->chan.device_node); + kfree(cchan); + } +} + +static int cppi41_add_chans(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + struct cppi41_channel *cchan; + int i; + int ret; + u32 n_chans; + + ret = of_property_read_u32(pdev->dev.of_node, "#dma-channels", + &n_chans); + if (ret) + return ret; + /* + * The channels can only be used as TX or as RX. So we add twice + * that much dma channels because USB can only do RX or TX. + */ + n_chans *= 2; + + for (i = 0; i < n_chans; i++) { + cchan = kzalloc(sizeof(*cchan), GFP_KERNEL); + if (!cchan) + goto err; + + cchan->cdd = cdd; + if (i & 1) { + cchan->gcr_reg = cdd->ctrl_mem + DMA_TXGCR(i >> 1); + cchan->is_tx = 1; + } else { + cchan->gcr_reg = cdd->ctrl_mem + DMA_RXGCR(i >> 1); + cchan->is_tx = 0; + } + cchan->port_num = i >> 1; + cchan->desc = &cdd->cd[i]; + cchan->desc_phys = cdd->descs_phys; + cchan->desc_phys += i * sizeof(struct cppi41_desc); + cchan->chan.device = &cdd->ddev; + list_add_tail(&cchan->chan.device_node, &cdd->ddev.channels); + } + cdd->first_td_desc = n_chans; + + return 0; +err: + cleanup_chans(cdd); + return -ENOMEM; +} + +static void purge_descs(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + unsigned int mem_decs; + int i; + + mem_decs = ALLOC_DECS_NUM * sizeof(struct cppi41_desc); + + for (i = 0; i < DESCS_AREAS; i++) { + + cppi_writel(0, cdd->qmgr_mem + QMGR_MEMBASE(i)); + cppi_writel(0, cdd->qmgr_mem + QMGR_MEMCTRL(i)); + + dma_free_coherent(&pdev->dev, mem_decs, cdd->cd, + cdd->descs_phys); + } +} + +static void disable_sched(struct cppi41_dd *cdd) +{ + cppi_writel(0, cdd->sched_mem + DMA_SCHED_CTRL); +} + +static void deinit_cpii41(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + disable_sched(cdd); + + purge_descs(pdev, cdd); + + cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM0_BASE); + cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM0_BASE); + dma_free_coherent(&pdev->dev, QMGR_SCRATCH_SIZE, cdd->qmgr_scratch, + cdd->scratch_phys); +} + +static int init_descs(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + unsigned int desc_size; + unsigned int mem_decs; + int i; + u32 reg; + u32 idx; + + BUILD_BUG_ON(sizeof(struct cppi41_desc) & + (sizeof(struct cppi41_desc) - 1)); + BUILD_BUG_ON(sizeof(struct cppi41_desc) < 32); + BUILD_BUG_ON(ALLOC_DECS_NUM < 32); + + desc_size = sizeof(struct cppi41_desc); + mem_decs = ALLOC_DECS_NUM * desc_size; + + idx = 0; + for (i = 0; i < DESCS_AREAS; i++) { + + reg = idx << QMGR_MEMCTRL_IDX_SH; + reg |= (ilog2(desc_size) - 5) << QMGR_MEMCTRL_DESC_SH; + reg |= ilog2(ALLOC_DECS_NUM) - 5; + + BUILD_BUG_ON(DESCS_AREAS != 1); + cdd->cd = dma_alloc_coherent(&pdev->dev, mem_decs, + &cdd->descs_phys, GFP_KERNEL); + if (!cdd->cd) + return -ENOMEM; + + cppi_writel(cdd->descs_phys, cdd->qmgr_mem + QMGR_MEMBASE(i)); + cppi_writel(reg, cdd->qmgr_mem + QMGR_MEMCTRL(i)); + + idx += ALLOC_DECS_NUM; + } + return 0; +} + +static void init_sched(struct cppi41_dd *cdd) +{ + unsigned ch; + unsigned word; + u32 reg; + + word = 0; + cppi_writel(0, cdd->sched_mem + DMA_SCHED_CTRL); + for (ch = 0; ch < 15 * 2; ch += 2) { + + reg = SCHED_ENTRY0_CHAN(ch); + reg |= SCHED_ENTRY1_CHAN(ch) | SCHED_ENTRY1_IS_RX; + + reg |= SCHED_ENTRY2_CHAN(ch + 1); + reg |= SCHED_ENTRY3_CHAN(ch + 1) | SCHED_ENTRY3_IS_RX; + cppi_writel(reg, cdd->sched_mem + DMA_SCHED_WORD(word)); + word++; + } + reg = 15 * 2 * 2 - 1; + reg |= DMA_SCHED_CTRL_EN; + cppi_writel(reg, cdd->sched_mem + DMA_SCHED_CTRL); +} + +static int init_cppi41(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + int ret; + + BUILD_BUG_ON(QMGR_SCRATCH_SIZE > ((1 << 14) - 1)); + cdd->qmgr_scratch = dma_alloc_coherent(&pdev->dev, QMGR_SCRATCH_SIZE, + &cdd->scratch_phys, GFP_KERNEL); + if (!cdd->qmgr_scratch) + return -ENOMEM; + + cppi_writel(cdd->scratch_phys, cdd->qmgr_mem + QMGR_LRAM0_BASE); + cppi_writel(QMGR_SCRATCH_SIZE, cdd->qmgr_mem + QMGR_LRAM_SIZE); + cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM1_BASE); + + ret = init_descs(pdev, cdd); + if (ret) + goto err_td; + + cppi_writel(cdd->td_queue.submit, cdd->ctrl_mem + DMA_TDFDQ); + init_sched(cdd); + return 0; +err_td: + deinit_cpii41(pdev, cdd); + return ret; +} + +static struct platform_driver cpp41_dma_driver; +/* + * The param format is: + * X Y + * X: Port + * Y: 0 = RX else TX + */ +#define INFO_PORT 0 +#define INFO_IS_TX 1 + +static bool cpp41_dma_filter_fn(struct dma_chan *chan, void *param) +{ + struct cppi41_channel *cchan; + struct cppi41_dd *cdd; + const struct chan_queues *queues; + u32 *num = param; + + if (chan->device->dev->driver != &cpp41_dma_driver.driver) + return false; + + cchan = to_cpp41_chan(chan); + + if (cchan->port_num != num[INFO_PORT]) + return false; + + if (cchan->is_tx && !num[INFO_IS_TX]) + return false; + cdd = cchan->cdd; + if (cchan->is_tx) + queues = cdd->queues_tx; + else + queues = cdd->queues_rx; + + BUILD_BUG_ON(ARRAY_SIZE(usb_queues_rx) != ARRAY_SIZE(usb_queues_tx)); + if (WARN_ON(cchan->port_num > ARRAY_SIZE(usb_queues_rx))) + return false; + + cchan->q_num = queues[cchan->port_num].submit; + cchan->q_comp_num = queues[cchan->port_num].complete; + return true; +} + +static struct of_dma_filter_info cpp41_dma_info = { + .filter_fn = cpp41_dma_filter_fn, +}; + +static struct dma_chan *cppi41_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct of_dma_filter_info *info = ofdma->of_dma_data; + + if (!info || !info->filter_fn) + return NULL; + + if (count != 2) + return NULL; + + return dma_request_channel(info->dma_cap, info->filter_fn, + &dma_spec->args[0]); +} + +static const struct cppi_glue_infos usb_infos = { + .isr = cppi41_irq, + .queues_rx = usb_queues_rx, + .queues_tx = usb_queues_tx, + .td_queue = { .submit = 31, .complete = 0 }, +}; + +static const struct of_device_id cppi41_dma_ids[] = { + { .compatible = "ti,am3359-cppi41", .data = &usb_infos}, + {}, +}; +MODULE_DEVICE_TABLE(of, cppi41_dma_ids); + +static const struct cppi_glue_infos *get_glue_info(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + + of_id = of_match_node(cppi41_dma_ids, pdev->dev.of_node); + if (!of_id) + return NULL; + return of_id->data; +} + +static int cppi41_dma_probe(struct platform_device *pdev) +{ + struct cppi41_dd *cdd; + const struct cppi_glue_infos *glue_info; + int irq; + int ret; + + glue_info = get_glue_info(pdev); + if (!glue_info) + return -EINVAL; + + cdd = kzalloc(sizeof(*cdd), GFP_KERNEL); + if (!cdd) + return -ENOMEM; + + dma_cap_set(DMA_SLAVE, cdd->ddev.cap_mask); + cdd->ddev.device_alloc_chan_resources = cppi41_dma_alloc_chan_resources; + cdd->ddev.device_free_chan_resources = cppi41_dma_free_chan_resources; + cdd->ddev.device_tx_status = cppi41_dma_tx_status; + cdd->ddev.device_issue_pending = cppi41_dma_issue_pending; + cdd->ddev.device_prep_slave_sg = cppi41_dma_prep_slave_sg; + cdd->ddev.device_control = cppi41_dma_control; + cdd->ddev.dev = &pdev->dev; + INIT_LIST_HEAD(&cdd->ddev.channels); + cpp41_dma_info.dma_cap = cdd->ddev.cap_mask; + + cdd->usbss_mem = of_iomap(pdev->dev.of_node, 0); + cdd->ctrl_mem = of_iomap(pdev->dev.of_node, 1); + cdd->sched_mem = of_iomap(pdev->dev.of_node, 2); + cdd->qmgr_mem = of_iomap(pdev->dev.of_node, 3); + + if (!cdd->usbss_mem || !cdd->ctrl_mem || !cdd->sched_mem || + !cdd->qmgr_mem) { + ret = -ENXIO; + goto err_remap; + } + + cdd->queues_rx = glue_info->queues_rx; + cdd->queues_tx = glue_info->queues_tx; + cdd->td_queue = glue_info->td_queue; + + ret = init_cppi41(pdev, cdd); + if (ret) + goto err_init_cppi; + + ret = cppi41_add_chans(pdev, cdd); + if (ret) + goto err_chans; + + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!irq) + goto err_irq; + + cppi_writel(USBSS_IRQ_PD_COMP, cdd->usbss_mem + USBSS_IRQ_ENABLER); + + ret = request_irq(irq, glue_info->isr, IRQF_SHARED, + dev_name(&pdev->dev), cdd); + if (ret) + goto err_irq; + cdd->irq = irq; + + ret = dma_async_device_register(&cdd->ddev); + if (ret) + goto err_dma_reg; + + ret = of_dma_controller_register(pdev->dev.of_node, + cppi41_dma_xlate, &cpp41_dma_info); + if (ret) + goto err_of; + + platform_set_drvdata(pdev, cdd); + return 0; +err_of: + dma_async_device_unregister(&cdd->ddev); +err_dma_reg: + free_irq(irq, cdd); +err_irq: + cppi_writel(0, cdd->usbss_mem + USBSS_IRQ_CLEARR); + cleanup_chans(cdd); +err_chans: + deinit_cpii41(pdev, cdd); +err_init_cppi: + iounmap(cdd->usbss_mem); + iounmap(cdd->ctrl_mem); + iounmap(cdd->sched_mem); + iounmap(cdd->qmgr_mem); +err_remap: + kfree(cdd); + return ret; +} + +static int cppi41_dma_remove(struct platform_device *pdev) +{ + struct cppi41_dd *cdd = platform_get_drvdata(pdev); + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&cdd->ddev); + + cppi_writel(0, cdd->usbss_mem + USBSS_IRQ_CLEARR); + free_irq(cdd->irq, cdd); + cleanup_chans(cdd); + deinit_cpii41(pdev, cdd); + iounmap(cdd->usbss_mem); + iounmap(cdd->ctrl_mem); + iounmap(cdd->sched_mem); + iounmap(cdd->qmgr_mem); + kfree(cdd); + return 0; +} + +static struct platform_driver cpp41_dma_driver = { + .probe = cppi41_dma_probe, + .remove = cppi41_dma_remove, + .driver = { + .name = "cppi41-dma-engine", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cppi41_dma_ids), + }, +}; + +module_platform_driver(cpp41_dma_driver); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sebastian Andrzej Siewior "); diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index b7257ae038fd..04658d7666e9 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -129,6 +129,10 @@ config USB_TI_CPPI_DMA help Enable DMA transfers when TI CPPI DMA is available. +config USB_TI_CPPI41_DMA + bool 'TI CPPI 4.1 (AM335x)' + depends on ARCH_OMAP + config USB_TUSB_OMAP_DMA bool 'TUSB 6010' depends on USB_MUSB_TUSB6010 diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 52f552c1ba2b..c5ea5c6dc169 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -32,3 +32,4 @@ musb_hdrc-$(CONFIG_USB_INVENTRA_DMA) += musbhsdma.o musb_hdrc-$(CONFIG_USB_TI_CPPI_DMA) += cppi_dma.o musb_hdrc-$(CONFIG_USB_TUSB_OMAP_DMA) += tusb6010_omap.o musb_hdrc-$(CONFIG_USB_UX500_DMA) += ux500_dma.o +musb_hdrc-$(CONFIG_USB_TI_CPPI41_DMA) += musb_cppi41.o diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c new file mode 100644 index 000000000000..a74259ea0306 --- /dev/null +++ b/drivers/usb/musb/musb_cppi41.c @@ -0,0 +1,552 @@ +#include +#include +#include +#include +#include +#include + +#include "musb_core.h" + +#define RNDIS_REG(x) (0x80 + ((x - 1) * 4)) + +#define EP_MODE_AUTOREG_NONE 0 +#define EP_MODE_AUTOREG_ALL_NEOP 1 +#define EP_MODE_AUTOREG_ALWAYS 3 + +#define EP_MODE_DMA_TRANSPARENT 0 +#define EP_MODE_DMA_RNDIS 1 +#define EP_MODE_DMA_GEN_RNDIS 3 + +#define USB_CTRL_TX_MODE 0x70 +#define USB_CTRL_RX_MODE 0x74 +#define USB_CTRL_AUTOREQ 0xd0 +#define USB_TDOWN 0xd8 + +struct cppi41_dma_channel { + struct dma_channel channel; + struct cppi41_dma_controller *controller; + struct musb_hw_ep *hw_ep; + struct dma_chan *dc; + dma_cookie_t cookie; + u8 port_num; + u8 is_tx; + u8 is_allocated; + u8 usb_toggle; + + dma_addr_t buf_addr; + u32 total_len; + u32 prog_len; + u32 transferred; + u32 packet_sz; +}; + +#define MUSB_DMA_NUM_CHANNELS 15 + +struct cppi41_dma_controller { + struct dma_controller controller; + struct cppi41_dma_channel rx_channel[MUSB_DMA_NUM_CHANNELS]; + struct cppi41_dma_channel tx_channel[MUSB_DMA_NUM_CHANNELS]; + struct musb *musb; + u32 rx_mode; + u32 tx_mode; + u32 auto_req; +}; + +static void save_rx_toggle(struct cppi41_dma_channel *cppi41_channel) +{ + u16 csr; + u8 toggle; + + if (cppi41_channel->is_tx) + return; + if (!is_host_active(cppi41_channel->controller->musb)) + return; + + csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR); + toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0; + + cppi41_channel->usb_toggle = toggle; +} + +static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel) +{ + u16 csr; + u8 toggle; + + if (cppi41_channel->is_tx) + return; + if (!is_host_active(cppi41_channel->controller->musb)) + return; + + csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR); + toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0; + + /* + * AM335x Advisory 1.0.13: Due to internal synchronisation error the + * data toggle may reset from DATA1 to DATA0 during receiving data from + * more than one endpoint. + */ + if (!toggle && toggle == cppi41_channel->usb_toggle) { + csr |= MUSB_RXCSR_H_DATATOGGLE | MUSB_RXCSR_H_WR_DATATOGGLE; + musb_writew(cppi41_channel->hw_ep->regs, MUSB_RXCSR, csr); + dev_dbg(cppi41_channel->controller->musb->controller, + "Restoring DATA1 toggle.\n"); + } + + cppi41_channel->usb_toggle = toggle; +} + +static void cppi41_dma_callback(void *private_data) +{ + struct dma_channel *channel = private_data; + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + struct musb *musb = hw_ep->musb; + unsigned long flags; + struct dma_tx_state txstate; + u32 transferred; + + spin_lock_irqsave(&musb->lock, flags); + + dmaengine_tx_status(cppi41_channel->dc, cppi41_channel->cookie, + &txstate); + transferred = cppi41_channel->prog_len - txstate.residue; + cppi41_channel->transferred += transferred; + + dev_dbg(musb->controller, "DMA transfer done on hw_ep=%d bytes=%d/%d\n", + hw_ep->epnum, cppi41_channel->transferred, + cppi41_channel->total_len); + + update_rx_toggle(cppi41_channel); + + if (cppi41_channel->transferred == cppi41_channel->total_len || + transferred < cppi41_channel->packet_sz) { + + /* done, complete */ + cppi41_channel->channel.actual_len = + cppi41_channel->transferred; + cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE; + musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx); + } else { + /* next iteration, reload */ + struct dma_chan *dc = cppi41_channel->dc; + struct dma_async_tx_descriptor *dma_desc; + enum dma_transfer_direction direction; + u16 csr; + u32 remain_bytes; + void __iomem *epio = cppi41_channel->hw_ep->regs; + + cppi41_channel->buf_addr += cppi41_channel->packet_sz; + + remain_bytes = cppi41_channel->total_len; + remain_bytes -= cppi41_channel->transferred; + remain_bytes = min(remain_bytes, cppi41_channel->packet_sz); + cppi41_channel->prog_len = remain_bytes; + + direction = cppi41_channel->is_tx ? DMA_MEM_TO_DEV + : DMA_DEV_TO_MEM; + dma_desc = dmaengine_prep_slave_single(dc, + cppi41_channel->buf_addr, + remain_bytes, + direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (WARN_ON(!dma_desc)) + return; + + dma_desc->callback = cppi41_dma_callback; + dma_desc->callback_param = channel; + cppi41_channel->cookie = dma_desc->tx_submit(dma_desc); + dma_async_issue_pending(dc); + + if (!cppi41_channel->is_tx) { + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, csr); + } + } + spin_unlock_irqrestore(&musb->lock, flags); +} + +static u32 update_ep_mode(unsigned ep, unsigned mode, u32 old) +{ + unsigned shift; + + shift = (ep - 1) * 2; + old &= ~(3 << shift); + old |= mode << shift; + return old; +} + +static void cppi41_set_dma_mode(struct cppi41_dma_channel *cppi41_channel, + unsigned mode) +{ + struct cppi41_dma_controller *controller = cppi41_channel->controller; + u32 port; + u32 new_mode; + u32 old_mode; + + if (cppi41_channel->is_tx) + old_mode = controller->tx_mode; + else + old_mode = controller->rx_mode; + port = cppi41_channel->port_num; + new_mode = update_ep_mode(port, mode, old_mode); + + if (new_mode == old_mode) + return; + if (cppi41_channel->is_tx) { + controller->tx_mode = new_mode; + musb_writel(controller->musb->ctrl_base, USB_CTRL_TX_MODE, + new_mode); + } else { + controller->rx_mode = new_mode; + musb_writel(controller->musb->ctrl_base, USB_CTRL_RX_MODE, + new_mode); + } +} + +static void cppi41_set_autoreq_mode(struct cppi41_dma_channel *cppi41_channel, + unsigned mode) +{ + struct cppi41_dma_controller *controller = cppi41_channel->controller; + u32 port; + u32 new_mode; + u32 old_mode; + + old_mode = controller->auto_req; + port = cppi41_channel->port_num; + new_mode = update_ep_mode(port, mode, old_mode); + + if (new_mode == old_mode) + return; + controller->auto_req = new_mode; + musb_writel(controller->musb->ctrl_base, USB_CTRL_AUTOREQ, new_mode); +} + +static bool cppi41_configure_channel(struct dma_channel *channel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + struct dma_chan *dc = cppi41_channel->dc; + struct dma_async_tx_descriptor *dma_desc; + enum dma_transfer_direction direction; + struct musb *musb = cppi41_channel->controller->musb; + unsigned use_gen_rndis = 0; + + dev_dbg(musb->controller, + "configure ep%d/%x packet_sz=%d, mode=%d, dma_addr=0x%llx, len=%d is_tx=%d\n", + cppi41_channel->port_num, RNDIS_REG(cppi41_channel->port_num), + packet_sz, mode, (unsigned long long) dma_addr, + len, cppi41_channel->is_tx); + + cppi41_channel->buf_addr = dma_addr; + cppi41_channel->total_len = len; + cppi41_channel->transferred = 0; + cppi41_channel->packet_sz = packet_sz; + + /* + * Due to AM335x' Advisory 1.0.13 we are not allowed to transfer more + * than max packet size at a time. + */ + if (cppi41_channel->is_tx) + use_gen_rndis = 1; + + if (use_gen_rndis) { + /* RNDIS mode */ + if (len > packet_sz) { + musb_writel(musb->ctrl_base, + RNDIS_REG(cppi41_channel->port_num), len); + /* gen rndis */ + cppi41_set_dma_mode(cppi41_channel, + EP_MODE_DMA_GEN_RNDIS); + + /* auto req */ + cppi41_set_autoreq_mode(cppi41_channel, + EP_MODE_AUTOREG_ALL_NEOP); + } else { + musb_writel(musb->ctrl_base, + RNDIS_REG(cppi41_channel->port_num), 0); + cppi41_set_dma_mode(cppi41_channel, + EP_MODE_DMA_TRANSPARENT); + cppi41_set_autoreq_mode(cppi41_channel, + EP_MODE_AUTOREG_NONE); + } + } else { + /* fallback mode */ + cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT); + cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREG_NONE); + len = min_t(u32, packet_sz, len); + } + cppi41_channel->prog_len = len; + direction = cppi41_channel->is_tx ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + dma_desc = dmaengine_prep_slave_single(dc, dma_addr, len, direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!dma_desc) + return false; + + dma_desc->callback = cppi41_dma_callback; + dma_desc->callback_param = channel; + cppi41_channel->cookie = dma_desc->tx_submit(dma_desc); + + save_rx_toggle(cppi41_channel); + dma_async_issue_pending(dc); + return true; +} + +static struct dma_channel *cppi41_dma_channel_allocate(struct dma_controller *c, + struct musb_hw_ep *hw_ep, u8 is_tx) +{ + struct cppi41_dma_controller *controller = container_of(c, + struct cppi41_dma_controller, controller); + struct cppi41_dma_channel *cppi41_channel = NULL; + u8 ch_num = hw_ep->epnum - 1; + + if (ch_num >= MUSB_DMA_NUM_CHANNELS) + return NULL; + + if (is_tx) + cppi41_channel = &controller->tx_channel[ch_num]; + else + cppi41_channel = &controller->rx_channel[ch_num]; + + if (!cppi41_channel->dc) + return NULL; + + if (cppi41_channel->is_allocated) + return NULL; + + cppi41_channel->hw_ep = hw_ep; + cppi41_channel->is_allocated = 1; + + return &cppi41_channel->channel; +} + +static void cppi41_dma_channel_release(struct dma_channel *channel) +{ + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + + if (cppi41_channel->is_allocated) { + cppi41_channel->is_allocated = 0; + channel->status = MUSB_DMA_STATUS_FREE; + channel->actual_len = 0; + } +} + +static int cppi41_dma_channel_program(struct dma_channel *channel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + int ret; + + BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || + channel->status == MUSB_DMA_STATUS_BUSY); + + channel->status = MUSB_DMA_STATUS_BUSY; + channel->actual_len = 0; + ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len); + if (!ret) + channel->status = MUSB_DMA_STATUS_FREE; + + return ret; +} + +static int cppi41_is_compatible(struct dma_channel *channel, u16 maxpacket, + void *buf, u32 length) +{ + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + struct cppi41_dma_controller *controller = cppi41_channel->controller; + struct musb *musb = controller->musb; + + if (is_host_active(musb)) { + WARN_ON(1); + return 1; + } + return 0; +} + +static int cppi41_dma_channel_abort(struct dma_channel *channel) +{ + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + struct cppi41_dma_controller *controller = cppi41_channel->controller; + struct musb *musb = controller->musb; + void __iomem *epio = cppi41_channel->hw_ep->regs; + int tdbit; + int ret; + unsigned is_tx; + u16 csr; + + is_tx = cppi41_channel->is_tx; + dev_dbg(musb->controller, "abort channel=%d, is_tx=%d\n", + cppi41_channel->port_num, is_tx); + + if (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE) + return 0; + + if (is_tx) { + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~MUSB_TXCSR_DMAENAB; + musb_writew(epio, MUSB_TXCSR, csr); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB); + musb_writew(epio, MUSB_RXCSR, csr); + + csr = musb_readw(epio, MUSB_RXCSR); + if (csr & MUSB_RXCSR_RXPKTRDY) { + csr |= MUSB_RXCSR_FLUSHFIFO; + musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + } + } + + tdbit = 1 << cppi41_channel->port_num; + if (is_tx) + tdbit <<= 16; + + do { + musb_writel(musb->ctrl_base, USB_TDOWN, tdbit); + ret = dmaengine_terminate_all(cppi41_channel->dc); + } while (ret == -EAGAIN); + + musb_writel(musb->ctrl_base, USB_TDOWN, tdbit); + + if (is_tx) { + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_TXPKTRDY) { + csr |= MUSB_TXCSR_FLUSHFIFO; + musb_writew(epio, MUSB_TXCSR, csr); + } + } + + cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE; + return 0; +} + +static void cppi41_release_all_dma_chans(struct cppi41_dma_controller *ctrl) +{ + struct dma_chan *dc; + int i; + + for (i = 0; i < MUSB_DMA_NUM_CHANNELS; i++) { + dc = ctrl->tx_channel[i].dc; + if (dc) + dma_release_channel(dc); + dc = ctrl->rx_channel[i].dc; + if (dc) + dma_release_channel(dc); + } +} + +static void cppi41_dma_controller_stop(struct cppi41_dma_controller *controller) +{ + cppi41_release_all_dma_chans(controller); +} + +static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) +{ + struct musb *musb = controller->musb; + struct device *dev = musb->controller; + struct device_node *np = dev->of_node; + struct cppi41_dma_channel *cppi41_channel; + int count; + int i; + int ret; + + count = of_property_count_strings(np, "dma-names"); + if (count < 0) + return count; + + for (i = 0; i < count; i++) { + struct dma_chan *dc; + struct dma_channel *musb_dma; + const char *str; + unsigned is_tx; + unsigned int port; + + ret = of_property_read_string_index(np, "dma-names", i, &str); + if (ret) + goto err; + if (!strncmp(str, "tx", 2)) + is_tx = 1; + else if (!strncmp(str, "rx", 2)) + is_tx = 0; + else { + dev_err(dev, "Wrong dmatype %s\n", str); + goto err; + } + ret = kstrtouint(str + 2, 0, &port); + if (ret) + goto err; + + if (port > MUSB_DMA_NUM_CHANNELS || !port) + goto err; + if (is_tx) + cppi41_channel = &controller->tx_channel[port - 1]; + else + cppi41_channel = &controller->rx_channel[port - 1]; + + cppi41_channel->controller = controller; + cppi41_channel->port_num = port; + cppi41_channel->is_tx = is_tx; + + musb_dma = &cppi41_channel->channel; + musb_dma->private_data = cppi41_channel; + musb_dma->status = MUSB_DMA_STATUS_FREE; + musb_dma->max_len = SZ_4M; + + dc = dma_request_slave_channel(dev, str); + if (!dc) { + dev_err(dev, "Falied to request %s.\n", str); + goto err; + } + cppi41_channel->dc = dc; + } + return 0; +err: + cppi41_release_all_dma_chans(controller); + return -EINVAL; +} + +void dma_controller_destroy(struct dma_controller *c) +{ + struct cppi41_dma_controller *controller = container_of(c, + struct cppi41_dma_controller, controller); + + cppi41_dma_controller_stop(controller); + kfree(controller); +} + +struct dma_controller *dma_controller_create(struct musb *musb, + void __iomem *base) +{ + struct cppi41_dma_controller *controller; + int ret; + + if (!musb->controller->of_node) { + dev_err(musb->controller, "Need DT for the DMA engine.\n"); + return NULL; + } + + controller = kzalloc(sizeof(*controller), GFP_KERNEL); + if (!controller) + goto kzalloc_fail; + + controller->musb = musb; + + controller->controller.channel_alloc = cppi41_dma_channel_allocate; + controller->controller.channel_release = cppi41_dma_channel_release; + controller->controller.channel_program = cppi41_dma_channel_program; + controller->controller.channel_abort = cppi41_dma_channel_abort; + controller->controller.is_compatible = cppi41_is_compatible; + + ret = cppi41_dma_controller_start(controller); + if (ret) + goto plat_get_fail; + return &controller->controller; + +plat_get_fail: + kfree(controller); +kzalloc_fail: + return NULL; +} diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index c8e67fde2156..1345a4ff041a 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -68,7 +68,7 @@ struct musb_hw_ep; #define is_dma_capable() (1) #endif -#ifdef CONFIG_USB_TI_CPPI_DMA +#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) #define is_cppi_enabled() 1 #else #define is_cppi_enabled() 0 -- cgit v1.2.3-58-ga151 From ebe864a6cb8e087ede047fa1fa6b6d06fcb9a9e4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 29 Apr 2013 22:18:01 +0200 Subject: usb: gadget: uvc: Fix error handling in uvc_queue_buffer() The conversion to videobuf2 failed to check the return value of vb2_qbuf(). Fix it. Cc: stable@vger.kernel.org Reported-by: Michael Grzeschik Signed-off-by: Laurent Pinchart Tested-By: Michael Grzeschik Signed-off-by: Felipe Balbi --- drivers/usb/gadget/uvc_queue.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index e6170478ea9f..0bb5d50075de 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -193,12 +193,16 @@ static int uvc_queue_buffer(struct uvc_video_queue *queue, mutex_lock(&queue->mutex); ret = vb2_qbuf(&queue->queue, buf); + if (ret < 0) + goto done; + spin_lock_irqsave(&queue->irqlock, flags); ret = (queue->flags & UVC_QUEUE_PAUSED) != 0; queue->flags &= ~UVC_QUEUE_PAUSED; spin_unlock_irqrestore(&queue->irqlock, flags); - mutex_unlock(&queue->mutex); +done: + mutex_unlock(&queue->mutex); return ret; } -- cgit v1.2.3-58-ga151 From 2cdcec4fedd6a5ee77bd551e6be7505f2230cd43 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 12 Aug 2013 16:06:49 +0300 Subject: usb: host: add has_tdi_phy_lpm capability bit The has_hostpc capability bit indicates that the host controller has the HOSTPC register extensions, but at the same time enables clock disabling power saving features with the PHY Low Power Clock Disable (PHCD) bit. However, some host controllers have the HOSTPC extensions but don't support the low-power feature, so the PHCD bit must not be set on those controllers. Add a separate capability bit for the low-power feature instead, and change all existing users of has_hostpc to use this new capability bit. The idea for this commit is taken from an old 2012 commit that never got merged ("disociate chipidea PHY low power suspend control from hostpc") Inspired-by: Matthieu CASTET Signed-off-by: Tuomas Tynkkynen Acked-by: Alan Stern Tested-by: Stephen Warren Reviewed-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/chipidea/host.c | 1 + drivers/usb/host/ehci-hub.c | 14 +++++++------- drivers/usb/host/ehci.h | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 40d0fda4f66c..9b3aaf132bc3 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -63,6 +63,7 @@ static int host_start(struct ci_hdrc *ci) ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; ehci->has_hostpc = ci->hw_bank.lpm; + ehci->has_tdi_phy_lpm = ci->hw_bank.lpm; ret = usb_add_hcd(hcd, 0, 0); if (ret) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 6dce37555c4f..6280bd269e12 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -183,7 +183,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, spin_lock_irq(&ehci->lock); /* clear phy low-power mode before changing wakeup flags */ - if (ehci->has_hostpc) { + if (ehci->has_tdi_phy_lpm) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; @@ -217,7 +217,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, } /* enter phy low-power mode again */ - if (ehci->has_hostpc) { + if (ehci->has_tdi_phy_lpm) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; @@ -309,7 +309,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } } - if (changed && ehci->has_hostpc) { + if (changed && ehci->has_tdi_phy_lpm) { spin_unlock_irq(&ehci->lock); msleep(5); /* 5 ms for HCD to enter low-power mode */ spin_lock_irq(&ehci->lock); @@ -435,7 +435,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) goto shutdown; /* clear phy low-power mode before resume */ - if (ehci->bus_suspended && ehci->has_hostpc) { + if (ehci->bus_suspended && ehci->has_tdi_phy_lpm) { i = HCS_N_PORTS(ehci->hcs_params); while (i--) { if (test_bit(i, &ehci->bus_suspended)) { @@ -788,7 +788,7 @@ static int ehci_hub_control ( goto error; /* clear phy low-power mode before resume */ - if (ehci->has_hostpc) { + if (ehci->has_tdi_phy_lpm) { temp1 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, hostpc_reg); @@ -1032,12 +1032,12 @@ static int ehci_hub_control ( /* After above check the port must be connected. * Set appropriate bit thus could put phy into low power - * mode if we have hostpc feature + * mode if we have tdi_phy_lpm feature */ temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - if (ehci->has_hostpc) { + if (ehci->has_tdi_phy_lpm) { spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ spin_lock_irqsave(&ehci->lock, flags); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 64f9a08e959c..d034d94a7fea 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -210,6 +210,7 @@ struct ehci_hcd { /* one per controller */ #define OHCI_HCCTRL_LEN 0x4 __hc32 *ohci_hcctrl_reg; unsigned has_hostpc:1; + unsigned has_tdi_phy_lpm:1; unsigned has_ppcd:1; /* support per-port change bits */ u8 sbrn; /* packed release number */ -- cgit v1.2.3-58-ga151 From f5833a0bde5d7795b19f8a881278e5506ab5764b Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 12 Aug 2013 16:06:50 +0300 Subject: usb: phy: tegra: Fix wrong PHY parameters Some of the PHY parameters are not set according to the TRMs: - UTMIP_FS_PREABMLE_J should be set, not cleared - UTMIP_XCVR_LSBIAS_SEL should be cleared, not set - UTMIP_PD_CHRG should be set in host mode and cleared in device mode - UTMIP_XCVR_SETUP is a two-part field; the upper bits were not set properly Signed-off-by: Tuomas Tynkkynen Tested-by: Stephen Warren Reviewed-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 49fa2da56c4b..ebbf85fdfc7f 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -86,11 +86,13 @@ #define UTMIP_XCVR_CFG0 0x808 #define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0) +#define UTMIP_XCVR_SETUP_MSB(x) ((((x) & 0x70) >> 4) << 22) #define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8) #define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10) #define UTMIP_FORCE_PD_POWERDOWN (1 << 14) #define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) #define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) +#define UTMIP_XCVR_LSBIAS_SEL (1 << 21) #define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25) #define UTMIP_BIAS_CFG0 0x80c @@ -342,7 +344,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) } val = readl(base + UTMIP_TX_CFG0); - val &= ~UTMIP_FS_PREABMLE_J; + val |= UTMIP_FS_PREABMLE_J; writel(val, base + UTMIP_TX_CFG0); val = readl(base + UTMIP_HSRX_CFG0); @@ -381,16 +383,26 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val = readl(base + USB_SUSP_CTRL); val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV); writel(val, base + USB_SUSP_CTRL); + + val = readl(base + UTMIP_BAT_CHRG_CFG0); + val &= ~UTMIP_PD_CHRG; + writel(val, base + UTMIP_BAT_CHRG_CFG0); + } else { + val = readl(base + UTMIP_BAT_CHRG_CFG0); + val |= UTMIP_PD_CHRG; + writel(val, base + UTMIP_BAT_CHRG_CFG0); } utmip_pad_power_on(phy); val = readl(base + UTMIP_XCVR_CFG0); val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | - UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) | + UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_LSBIAS_SEL | + UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_SETUP_MSB(~0) | UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) | UTMIP_XCVR_HSSLEW_MSB(~0)); val |= UTMIP_XCVR_SETUP(config->xcvr_setup); + val |= UTMIP_XCVR_SETUP_MSB(config->xcvr_setup); val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew); val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew); writel(val, base + UTMIP_XCVR_CFG0); @@ -401,10 +413,6 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj); writel(val, base + UTMIP_XCVR_CFG1); - val = readl(base + UTMIP_BAT_CHRG_CFG0); - val &= ~UTMIP_PD_CHRG; - writel(val, base + UTMIP_BAT_CHRG_CFG0); - val = readl(base + UTMIP_BIAS_CFG1); val &= ~UTMIP_BIAS_PDTRK_COUNT(~0); val |= UTMIP_BIAS_PDTRK_COUNT(0x5); -- cgit v1.2.3-58-ga151 From 3e635202ce40e4d7ff3fafc18db70c5d28cc6622 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 12 Aug 2013 16:06:51 +0300 Subject: usb: phy: tegra: Tegra30 support The Tegra30 USB PHY is a bit different than the Tegra20 PHY: - The EHCI controller supports the HOSTPC register extension, and some of the fields that the PHY needs to modify (PHCD and PTS) have moved to the new HOSTPC register. - Some of the UTMI PLL configuration registers have moved from the USB register space to the Clock-And-Reset controller space. In Tegra30 the clock driver is responsible for configuring the UTMI PLL. - The USBMODE register must be explicitly written to enter host mode. - Certain PHY parameters need to be programmed for optimal signal quality. Support for this will be added in the next patch. The new tegra_phy_soc_config structure is added to describe the differences between the SoCs. Signed-off-by: Tuomas Tynkkynen Tested-by: Stephen Warren Reviewed-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 121 +++++++++++++++++++++++++++++--------- include/linux/usb/tegra_usb_phy.h | 19 ++++++ 2 files changed, 112 insertions(+), 28 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index ebbf85fdfc7f..1ad184af7601 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -39,11 +40,16 @@ #define ULPI_VIEWPORT 0x170 -/* PORTSC registers */ +/* PORTSC PTS/PHCD bits, Tegra20 only */ #define TEGRA_USB_PORTSC1 0x184 #define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) #define TEGRA_USB_PORTSC1_PHCD (1 << 23) +/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */ +#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4 +#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) +#define TEGRA_USB_HOSTPC1_DEVLC_PHCD (1 << 22) + /* Bits of PORTSC1, which will get cleared by writing 1 into them */ #define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) @@ -141,6 +147,12 @@ #define UTMIP_BIAS_CFG1 0x83c #define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) +/* For Tegra30 and above only, the address is different in Tegra20 */ +#define USB_USBMODE 0x1f8 +#define USB_USBMODE_MASK (3 << 0) +#define USB_USBMODE_HOST (3 << 0) +#define USB_USBMODE_DEVICE (2 << 0) + static DEFINE_SPINLOCK(utmip_pad_lock); static int utmip_pad_count; @@ -193,10 +205,17 @@ static void set_pts(struct tegra_usb_phy *phy, u8 pts_val) void __iomem *base = phy->regs; unsigned long val; - val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; - val &= ~TEGRA_USB_PORTSC1_PTS(3); - val |= TEGRA_USB_PORTSC1_PTS(pts_val & 3); - writel(val, base + TEGRA_USB_PORTSC1); + if (phy->soc_config->has_hostpc) { + val = readl(base + TEGRA_USB_HOSTPC1_DEVLC); + val &= ~TEGRA_USB_HOSTPC1_DEVLC_PTS(~0); + val |= TEGRA_USB_HOSTPC1_DEVLC_PTS(pts_val); + writel(val, base + TEGRA_USB_HOSTPC1_DEVLC); + } else { + val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; + val &= ~TEGRA_USB_PORTSC1_PTS(~0); + val |= TEGRA_USB_PORTSC1_PTS(pts_val); + writel(val, base + TEGRA_USB_PORTSC1); + } } static void set_phcd(struct tegra_usb_phy *phy, bool enable) @@ -204,12 +223,21 @@ static void set_phcd(struct tegra_usb_phy *phy, bool enable) void __iomem *base = phy->regs; unsigned long val; - val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; - if (enable) - val |= TEGRA_USB_PORTSC1_PHCD; - else - val &= ~TEGRA_USB_PORTSC1_PHCD; - writel(val, base + TEGRA_USB_PORTSC1); + if (phy->soc_config->has_hostpc) { + val = readl(base + TEGRA_USB_HOSTPC1_DEVLC); + if (enable) + val |= TEGRA_USB_HOSTPC1_DEVLC_PHCD; + else + val &= ~TEGRA_USB_HOSTPC1_DEVLC_PHCD; + writel(val, base + TEGRA_USB_HOSTPC1_DEVLC); + } else { + val = readl(base + TEGRA_USB_PORTSC1) & ~PORT_RWC_BITS; + if (enable) + val |= TEGRA_USB_PORTSC1_PHCD; + else + val &= ~TEGRA_USB_PORTSC1_PHCD; + writel(val, base + TEGRA_USB_PORTSC1); + } } static int utmip_pad_open(struct tegra_usb_phy *phy) @@ -367,17 +395,21 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; writel(val, base + UTMIP_MISC_CFG0); - val = readl(base + UTMIP_MISC_CFG1); - val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0)); - val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) | - UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); - writel(val, base + UTMIP_MISC_CFG1); - - val = readl(base + UTMIP_PLL_CFG1); - val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); - val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | - UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); - writel(val, base + UTMIP_PLL_CFG1); + if (!phy->soc_config->utmi_pll_config_in_car_module) { + val = readl(base + UTMIP_MISC_CFG1); + val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | + UTMIP_PLLU_STABLE_COUNT(~0)); + val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) | + UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); + writel(val, base + UTMIP_MISC_CFG1); + + val = readl(base + UTMIP_PLL_CFG1); + val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | + UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); + val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | + UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); + writel(val, base + UTMIP_PLL_CFG1); + } if (phy->mode == USB_DR_MODE_PERIPHERAL) { val = readl(base + USB_SUSP_CTRL); @@ -448,6 +480,16 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) utmi_phy_clk_enable(phy); + if (phy->soc_config->requires_usbmode_setup) { + val = readl(base + USB_USBMODE); + val &= ~USB_USBMODE_MASK; + if (phy->mode == USB_DR_MODE_HOST) + val |= USB_USBMODE_HOST; + else + val |= USB_USBMODE_DEVICE; + writel(val, base + USB_USBMODE); + } + if (!phy->is_legacy_phy) set_pts(phy, 0); @@ -864,8 +906,30 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, return 0; } +static const struct tegra_phy_soc_config tegra20_soc_config = { + .utmi_pll_config_in_car_module = false, + .has_hostpc = false, + .requires_usbmode_setup = false, + .requires_extra_tuning_parameters = false, +}; + +static const struct tegra_phy_soc_config tegra30_soc_config = { + .utmi_pll_config_in_car_module = true, + .has_hostpc = true, + .requires_usbmode_setup = true, + .requires_extra_tuning_parameters = true, +}; + +static struct of_device_id tegra_usb_phy_id_table[] = { + { .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config }, + { .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table); + static int tegra_usb_phy_probe(struct platform_device *pdev) { + const struct of_device_id *match; struct resource *res; struct tegra_usb_phy *tegra_phy = NULL; struct device_node *np = pdev->dev.of_node; @@ -878,6 +942,13 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return -ENOMEM; } + match = of_match_device(tegra_usb_phy_id_table, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + tegra_phy->soc_config = match->data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "Failed to get I/O memory\n"); @@ -968,12 +1039,6 @@ static int tegra_usb_phy_remove(struct platform_device *pdev) return 0; } -static struct of_device_id tegra_usb_phy_id_table[] = { - { .compatible = "nvidia,tegra20-usb-phy", }, - { }, -}; -MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table); - static struct platform_driver tegra_usb_phy_driver = { .probe = tegra_usb_phy_probe, .remove = tegra_usb_phy_remove, diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index d3db274610a1..d3a63c354db9 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -18,6 +18,24 @@ #include #include +/* + * utmi_pll_config_in_car_module: true if the UTMI PLL configuration registers + * should be set up by clk-tegra, false if by the PHY code + * has_hostpc: true if the USB controller has the HOSTPC extension, which + * changes the location of the PHCD and PTS fields + * requires_usbmode_setup: true if the USBMODE register needs to be set to + * enter host mode + * requires_extra_tuning_parameters: true if xcvr_hsslew, hssquelch_level + * and hsdiscon_level should be set for adequate signal quality + */ + +struct tegra_phy_soc_config { + bool utmi_pll_config_in_car_module; + bool has_hostpc; + bool requires_usbmode_setup; + bool requires_extra_tuning_parameters; +}; + struct tegra_utmip_config { u8 hssync_start_delay; u8 elastic_limit; @@ -47,6 +65,7 @@ struct tegra_usb_phy { struct regulator *vbus; enum usb_dr_mode mode; void *config; + const struct tegra_phy_soc_config *soc_config; struct usb_phy *ulpi; struct usb_phy u_phy; bool is_legacy_phy; -- cgit v1.2.3-58-ga151 From e497a24d8e18af510879b2ae059ee20a4a58eae8 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 12 Aug 2013 16:06:53 +0300 Subject: usb: phy: tegra: Program new PHY parameters The Tegra30 TRM recommends configuration of certain PHY parameters for optimal quality. Program the following registers based on device tree parameters: - UTMIP_XCVR_HSSLEW: HS slew rate control. - UTMIP_HSSQUELCH_LEVEL: HS squelch detector level - UTMIP_HSDISCON_LEVEL: HS disconnect detector level. These registers exist in Tegra20, but programming them hasn't been necessary, so these parameters won't be set on Tegra20 to keep the device trees backward compatible. Additionally, the UTMIP_XCVR_SETUP parameter can be set from fuses instead of a software-programmed value, as the optimal value can vary between invidual boards. The boolean property nvidia,xcvr-setup-use-fuses can be used to enable this behaviour. Signed-off-by: Tuomas Tynkkynen Tested-by: Stephen Warren Reviewed-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 81 ++++++++++++++++++++++++++++++--------- include/linux/usb/tegra_usb_phy.h | 4 ++ 2 files changed, 67 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 1ad184af7601..3bfb3d1957c1 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -99,11 +99,15 @@ #define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) #define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) #define UTMIP_XCVR_LSBIAS_SEL (1 << 21) -#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25) +#define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4) +#define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25) #define UTMIP_BIAS_CFG0 0x80c #define UTMIP_OTGPD (1 << 11) #define UTMIP_BIASPD (1 << 10) +#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0) +#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2) +#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24) #define UTMIP_HSRX_CFG0 0x810 #define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) @@ -255,6 +259,7 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy) { unsigned long val, flags; void __iomem *base = phy->pad_regs; + struct tegra_utmip_config *config = phy->config; clk_prepare_enable(phy->pad_clk); @@ -263,6 +268,16 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy) if (utmip_pad_count++ == 0) { val = readl(base + UTMIP_BIAS_CFG0); val &= ~(UTMIP_OTGPD | UTMIP_BIASPD); + + if (phy->soc_config->requires_extra_tuning_parameters) { + val &= ~(UTMIP_HSSQUELCH_LEVEL(~0) | + UTMIP_HSDISCON_LEVEL(~0) | + UTMIP_HSDISCON_LEVEL_MSB(~0)); + + val |= UTMIP_HSSQUELCH_LEVEL(config->hssquelch_level); + val |= UTMIP_HSDISCON_LEVEL(config->hsdiscon_level); + val |= UTMIP_HSDISCON_LEVEL_MSB(config->hsdiscon_level); + } writel(val, base + UTMIP_BIAS_CFG0); } @@ -431,12 +446,20 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_LSBIAS_SEL | UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_SETUP_MSB(~0) | - UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) | - UTMIP_XCVR_HSSLEW_MSB(~0)); - val |= UTMIP_XCVR_SETUP(config->xcvr_setup); - val |= UTMIP_XCVR_SETUP_MSB(config->xcvr_setup); + UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0)); + + if (!config->xcvr_setup_use_fuses) { + val |= UTMIP_XCVR_SETUP(config->xcvr_setup); + val |= UTMIP_XCVR_SETUP_MSB(config->xcvr_setup); + } val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew); val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew); + + if (phy->soc_config->requires_extra_tuning_parameters) { + val &= ~(UTMIP_XCVR_HSSLEW(~0) | UTMIP_XCVR_HSSLEW_MSB(~0)); + val |= UTMIP_XCVR_HSSLEW(config->xcvr_hsslew); + val |= UTMIP_XCVR_HSSLEW_MSB(config->xcvr_hsslew); + } writel(val, base + UTMIP_XCVR_CFG0); val = readl(base + UTMIP_XCVR_CFG1); @@ -450,14 +473,14 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val |= UTMIP_BIAS_PDTRK_COUNT(0x5); writel(val, base + UTMIP_BIAS_CFG1); - if (phy->is_legacy_phy) { - val = readl(base + UTMIP_SPARE_CFG0); - if (phy->mode == USB_DR_MODE_PERIPHERAL) - val &= ~FUSE_SETUP_SEL; - else - val |= FUSE_SETUP_SEL; - writel(val, base + UTMIP_SPARE_CFG0); - } else { + val = readl(base + UTMIP_SPARE_CFG0); + if (config->xcvr_setup_use_fuses) + val |= FUSE_SETUP_SEL; + else + val &= ~FUSE_SETUP_SEL; + writel(val, base + UTMIP_SPARE_CFG0); + + if (!phy->is_legacy_phy) { val = readl(base + USB_SUSP_CTRL); val |= UTMIP_PHY_ENABLE; writel(val, base + USB_SUSP_CTRL); @@ -888,11 +911,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, if (err < 0) return err; - err = read_utmi_param(pdev, "nvidia,xcvr-setup", - &config->xcvr_setup); - if (err < 0) - return err; - err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew", &config->xcvr_lsfslew); if (err < 0) @@ -903,6 +921,33 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, if (err < 0) return err; + if (tegra_phy->soc_config->requires_extra_tuning_parameters) { + err = read_utmi_param(pdev, "nvidia,xcvr-hsslew", + &config->xcvr_hsslew); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,hssquelch-level", + &config->hssquelch_level); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,hsdiscon-level", + &config->hsdiscon_level); + if (err < 0) + return err; + } + + config->xcvr_setup_use_fuses = of_property_read_bool( + pdev->dev.of_node, "nvidia,xcvr-setup-use-fuses"); + + if (!config->xcvr_setup_use_fuses) { + err = read_utmi_param(pdev, "nvidia,xcvr-setup", + &config->xcvr_setup); + if (err < 0) + return err; + } + return 0; } diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index d3a63c354db9..1de16c324ec8 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -41,9 +41,13 @@ struct tegra_utmip_config { u8 elastic_limit; u8 idle_wait_delay; u8 term_range_adj; + bool xcvr_setup_use_fuses; u8 xcvr_setup; u8 xcvr_lsfslew; u8 xcvr_lsrslew; + u8 xcvr_hsslew; + u8 hssquelch_level; + u8 hsdiscon_level; }; enum tegra_usb_phy_port_speed { -- cgit v1.2.3-58-ga151 From 327d8b424578254f0c1e71f83960bf8934063529 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 12 Aug 2013 16:06:54 +0300 Subject: usb: host: tegra: Tegra30 support The Tegra30 EHCI controller is mostly compatible with the Tegra20 controller, except Tegra30 includes the HOSTPC register extension. The has_hostpc capability bit must be set in the ehci_hcd structure if the controller has such extensions. The new tegra_ehci_soc_config structure is added to describe the differences between the SoCs. Signed-off-by: Tuomas Tynkkynen Tested-by: Stephen Warren Reviewed-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/host/ehci-tegra.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index db8031fd5c13..78fa76da3324 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,10 @@ static struct hc_driver __read_mostly tegra_ehci_hc_driver; +struct tegra_ehci_soc_config { + bool has_hostpc; +}; + static int (*orig_hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength); @@ -320,8 +325,24 @@ static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) free_dma_aligned_buffer(urb); } +static const struct tegra_ehci_soc_config tegra30_soc_config = { + .has_hostpc = true, +}; + +static const struct tegra_ehci_soc_config tegra20_soc_config = { + .has_hostpc = false, +}; + +static struct of_device_id tegra_ehci_of_match[] = { + { .compatible = "nvidia,tegra30-ehci", .data = &tegra30_soc_config }, + { .compatible = "nvidia,tegra20-ehci", .data = &tegra20_soc_config }, + { }, +}; + static int tegra_ehci_probe(struct platform_device *pdev) { + const struct of_device_id *match; + const struct tegra_ehci_soc_config *soc_config; struct resource *res; struct usb_hcd *hcd; struct ehci_hcd *ehci; @@ -330,6 +351,13 @@ static int tegra_ehci_probe(struct platform_device *pdev) int irq; struct usb_phy *u_phy; + match = of_match_device(tegra_ehci_of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + soc_config = match->data; + /* Right now device-tree probed devices don't get dma_mask set. * Since shared usb code relies on it, set it here for now. * Once we have dma capability bindings this can go away. @@ -391,6 +419,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto cleanup_clk_en; } ehci->caps = hcd->regs + 0x100; + ehci->has_hostpc = soc_config->has_hostpc; err = usb_phy_init(hcd->phy); if (err) { @@ -468,11 +497,6 @@ static void tegra_ehci_hcd_shutdown(struct platform_device *pdev) hcd->driver->shutdown(hcd); } -static struct of_device_id tegra_ehci_of_match[] = { - { .compatible = "nvidia,tegra20-ehci", }, - { }, -}; - static struct platform_driver tegra_ehci_driver = { .probe = tegra_ehci_probe, .remove = tegra_ehci_remove, -- cgit v1.2.3-58-ga151 From 94dfd7edfd5c9b605caf7b562de7a813d216e011 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 3 Jul 2013 22:53:07 +0800 Subject: USB: HCD: support giveback of URB in tasklet context This patch implements the mechanism of giveback of URB in tasklet context, so that hardware interrupt handling time for usb host controller can be saved much, and HCD interrupt handling can be simplified. Motivations: 1), on some arch(such as ARM), DMA mapping/unmapping is a bit time-consuming, for example: when accessing usb mass storage via EHCI on pandaboard, the common length of transfer buffer is 120KB, the time consumed on DMA unmapping may reach hundreds of microseconds; even on A15 based box, the time is still about scores of microseconds 2), on some arch, reading DMA coherent memoery is very time-consuming, the most common example is usb video class driver[1] 3), driver's complete() callback may do much things which is driver specific, so the time is consumed unnecessarily in hardware irq context. 4), running driver's complete() callback in hardware irq context causes that host controller driver has to release its lock in interrupt handler, so reacquiring the lock after return may busy wait a while and increase interrupt handling time. More seriously, releasing the HCD lock makes HCD becoming quite complicated to deal with introduced races. So the patch proposes to run giveback of URB in tasklet context, then time consumed in HCD irq handling doesn't depend on drivers' complete and DMA mapping/unmapping any more, also we can simplify HCD since the HCD lock isn't needed to be released during irq handling. The patch should be reasonable and doable: 1), for drivers, they don't care if the complete() is called in hard irq context or softirq context 2), the biggest change is the situation in which usb_submit_urb() is called in complete() callback, so the introduced tasklet schedule delay might be a con, but it shouldn't be a big deal: - control/bulk asynchronous transfer isn't sensitive to schedule delay - the patch schedules giveback of periodic URBs using tasklet_hi_schedule, so the introduced delay should be very small - for ISOC transfer, generally, drivers submit several URBs concurrently to avoid interrupt delay, so it is OK with the little schedule delay. - for interrupt transfer, generally, drivers only submit one URB at the same time, but interrupt transfer is often used in event report, polling, ... situations, and a little delay should be OK. Considered that HCDs may optimize on submitting URB in complete(), the patch may cause the optimization not working, so introduces one flag to mark if the HCD supports to run giveback URB in tasklet context. When all HCDs are ready, the flag can be removed. [1], http://marc.info/?t=136438111600010&r=1&w=2 Cc: Oliver Neukum Acked-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 147 ++++++++++++++++++++++++++++++++++++++---------- include/linux/usb/hcd.h | 17 ++++++ 2 files changed, 134 insertions(+), 30 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index dc1346fee76c..0ffe016f4036 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -696,15 +696,7 @@ error: /* any errors get returned through the urb completion */ spin_lock_irq(&hcd_root_hub_lock); usb_hcd_unlink_urb_from_ep(hcd, urb); - - /* This peculiar use of spinlocks echoes what real HC drivers do. - * Avoiding calls to local_irq_disable/enable makes the code - * RT-friendly. - */ - spin_unlock(&hcd_root_hub_lock); usb_hcd_giveback_urb(hcd, urb, status); - spin_lock(&hcd_root_hub_lock); - spin_unlock_irq(&hcd_root_hub_lock); return 0; } @@ -744,9 +736,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) memcpy(urb->transfer_buffer, buffer, length); usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock(&hcd_root_hub_lock); usb_hcd_giveback_urb(hcd, urb, 0); - spin_lock(&hcd_root_hub_lock); } else { length = 0; set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); @@ -836,10 +826,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) if (urb == hcd->status_urb) { hcd->status_urb = NULL; usb_hcd_unlink_urb_from_ep(hcd, urb); - - spin_unlock(&hcd_root_hub_lock); usb_hcd_giveback_urb(hcd, urb, status); - spin_lock(&hcd_root_hub_lock); } } done: @@ -1656,6 +1643,72 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) /*-------------------------------------------------------------------------*/ +static void __usb_hcd_giveback_urb(struct urb *urb) +{ + struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus); + int status = urb->unlinked; + unsigned long flags; + + urb->hcpriv = NULL; + if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) && + urb->actual_length < urb->transfer_buffer_length && + !status)) + status = -EREMOTEIO; + + unmap_urb_for_dma(hcd, urb); + usbmon_urb_complete(&hcd->self, urb, status); + usb_unanchor_urb(urb); + + /* pass ownership to the completion handler */ + urb->status = status; + + /* + * We disable local IRQs here avoid possible deadlock because + * drivers may call spin_lock() to hold lock which might be + * acquired in one hard interrupt handler. + * + * The local_irq_save()/local_irq_restore() around complete() + * will be removed if current USB drivers have been cleaned up + * and no one may trigger the above deadlock situation when + * running complete() in tasklet. + */ + local_irq_save(flags); + urb->complete(urb); + local_irq_restore(flags); + + atomic_dec(&urb->use_count); + if (unlikely(atomic_read(&urb->reject))) + wake_up(&usb_kill_urb_queue); + usb_put_urb(urb); +} + +static void usb_giveback_urb_bh(unsigned long param) +{ + struct giveback_urb_bh *bh = (struct giveback_urb_bh *)param; + struct list_head local_list; + + spin_lock_irq(&bh->lock); + bh->running = true; + restart: + list_replace_init(&bh->head, &local_list); + spin_unlock_irq(&bh->lock); + + while (!list_empty(&local_list)) { + struct urb *urb; + + urb = list_entry(local_list.next, struct urb, urb_list); + list_del_init(&urb->urb_list); + __usb_hcd_giveback_urb(urb); + } + + /* check if there are new URBs to giveback */ + spin_lock_irq(&bh->lock); + if (!list_empty(&bh->head)) + goto restart; + bh->running = false; + spin_unlock_irq(&bh->lock); +} + /** * usb_hcd_giveback_urb - return URB from HCD to device driver * @hcd: host controller returning the URB @@ -1675,25 +1728,37 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) */ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) { - urb->hcpriv = NULL; - if (unlikely(urb->unlinked)) - status = urb->unlinked; - else if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) && - urb->actual_length < urb->transfer_buffer_length && - !status)) - status = -EREMOTEIO; + struct giveback_urb_bh *bh; + bool running, high_prio_bh; - unmap_urb_for_dma(hcd, urb); - usbmon_urb_complete(&hcd->self, urb, status); - usb_unanchor_urb(urb); + /* pass status to tasklet via unlinked */ + if (likely(!urb->unlinked)) + urb->unlinked = status; - /* pass ownership to the completion handler */ - urb->status = status; - urb->complete (urb); - atomic_dec (&urb->use_count); - if (unlikely(atomic_read(&urb->reject))) - wake_up (&usb_kill_urb_queue); - usb_put_urb (urb); + if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) { + __usb_hcd_giveback_urb(urb); + return; + } + + if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) { + bh = &hcd->high_prio_bh; + high_prio_bh = true; + } else { + bh = &hcd->low_prio_bh; + high_prio_bh = false; + } + + spin_lock(&bh->lock); + list_add_tail(&urb->urb_list, &bh->head); + running = bh->running; + spin_unlock(&bh->lock); + + if (running) + ; + else if (high_prio_bh) + tasklet_hi_schedule(&bh->bh); + else + tasklet_schedule(&bh->bh); } EXPORT_SYMBOL_GPL(usb_hcd_giveback_urb); @@ -2322,6 +2387,14 @@ EXPORT_SYMBOL_GPL (usb_hc_died); /*-------------------------------------------------------------------------*/ +static void init_giveback_urb_bh(struct giveback_urb_bh *bh) +{ + + spin_lock_init(&bh->lock); + INIT_LIST_HEAD(&bh->head); + tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh); +} + /** * usb_create_shared_hcd - create and initialize an HCD structure * @driver: HC driver that will use this hcd @@ -2590,6 +2663,10 @@ int usb_add_hcd(struct usb_hcd *hcd, && device_can_wakeup(&hcd->self.root_hub->dev)) dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); + /* initialize tasklets */ + init_giveback_urb_bh(&hcd->high_prio_bh); + init_giveback_urb_bh(&hcd->low_prio_bh); + /* enable irqs just before we start the controller, * if the BIOS provides legacy PCI irqs. */ @@ -2698,6 +2775,16 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_disconnect(&rhdev); /* Sets rhdev to NULL */ mutex_unlock(&usb_bus_list_lock); + /* + * tasklet_kill() isn't needed here because: + * - driver's disconnect() called from usb_disconnect() should + * make sure its URBs are completed during the disconnect() + * callback + * + * - it is too late to run complete() here since driver may have + * been removed already now + */ + /* Prevent any more root-hub status calls from the timer. * The HCD might still restart the timer (if a port status change * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 1e88377e22f4..a9c7d44bd2ed 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -22,6 +22,7 @@ #ifdef __KERNEL__ #include +#include #define MAX_TOPO_LEVEL 6 @@ -67,6 +68,13 @@ /*-------------------------------------------------------------------------*/ +struct giveback_urb_bh { + bool running; + spinlock_t lock; + struct list_head head; + struct tasklet_struct bh; +}; + struct usb_hcd { /* @@ -139,6 +147,9 @@ struct usb_hcd { resource_size_t rsrc_len; /* memory/io resource length */ unsigned power_budget; /* in mA, 0 = no limit */ + struct giveback_urb_bh high_prio_bh; + struct giveback_urb_bh low_prio_bh; + /* bandwidth_mutex should be taken before adding or removing * any new bus bandwidth constraints: * 1. Before adding a configuration for a new device. @@ -221,6 +232,7 @@ struct hc_driver { #define HCD_USB25 0x0030 /* Wireless USB 1.0 (USB 2.5)*/ #define HCD_USB3 0x0040 /* USB 3.0 */ #define HCD_MASK 0x0070 +#define HCD_BH 0x0100 /* URB complete in BH context */ /* called to init HCD and root hub */ int (*reset) (struct usb_hcd *hcd); @@ -361,6 +373,11 @@ struct hc_driver { int (*find_raw_port_number)(struct usb_hcd *, int); }; +static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) +{ + return hcd->driver->flags & HCD_BH; +} + extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb, int status); -- cgit v1.2.3-58-ga151 From 35371e4fbc3e1863a6e7a79b8c17c25cc96a1380 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 3 Jul 2013 22:53:09 +0800 Subject: USB: EHCI: improve ehci_endpoint_disable The patch does the below improvement: - think QH_STATE_COMPLETING as unlinking state since all URBs on the endpoint should be in unlinking or unlinked when doing endpoint_disable() - add "WARN_ON(!list_empty(&qh->qtd_list));" if qh->qh_state is QH_STATE_LINKED because there shouldn't be any active transfer in qh - when qh->qh_state is QH_STATE_LINKED, the QH(async or periodic) should be in its corresponding list, so the search through the async list isn't necessary. - unlink periodic QH to speed up unlinking if the QH is in linked state Basically, only the last one is related with this patchset because the assumption of "periodic qh self-unlinks on empty" isn't true any more when we introduce unlink-wait for periodic qh. Acked-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7abf1ce3a670..387cedf2b698 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -942,7 +942,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); unsigned long flags; - struct ehci_qh *qh, *tmp; + struct ehci_qh *qh; /* ASSERT: any requests/urbs are being unlinked */ /* ASSERT: nobody can be submitting urbs for this any more */ @@ -972,17 +972,13 @@ rescan: qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { case QH_STATE_LINKED: - case QH_STATE_COMPLETING: - for (tmp = ehci->async->qh_next.qh; - tmp && tmp != qh; - tmp = tmp->qh_next.qh) - continue; - /* periodic qh self-unlinks on empty, and a COMPLETING qh - * may already be unlinked. - */ - if (tmp) + WARN_ON(!list_empty(&qh->qtd_list)); + if (usb_endpoint_type(&ep->desc) != USB_ENDPOINT_XFER_INT) start_unlink_async(ehci, qh); + else + start_unlink_intr(ehci, qh); /* FALL THROUGH */ + case QH_STATE_COMPLETING: /* already in unlinking */ case QH_STATE_UNLINK: /* wait for hw to finish? */ case QH_STATE_UNLINK_WAIT: idle_timeout: -- cgit v1.2.3-58-ga151 From 9118f9eb4f1e97a135de3f78853c411befcf9775 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 3 Jul 2013 22:53:10 +0800 Subject: USB: EHCI: improve interrupt qh unlink ehci-hcd currently unlinks an interrupt QH when it becomes empty, that is, after its last URB completes. This works well because in almost all cases, the completion handler for an interrupt URB resubmits the URB; therefore the QH doesn't become empty and doesn't get unlinked. When we start using tasklets for URB completion, this scheme won't work as well. The resubmission won't occur until the tasklet runs, which will be some time after the completion is queued with the tasklet. During that delay, the QH will be empty and so will be unlinked unnecessarily. To prevent this problem, this patch adds a 5-ms time delay before empty interrupt QHs are unlinked. Most often, during that time the interrupt URB will be resubmitted and thus we can avoid unlinking the QH. Signed-off-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 1 + drivers/usb/host/ehci-hub.c | 1 + drivers/usb/host/ehci-mem.c | 1 + drivers/usb/host/ehci-sched.c | 47 +++++++++++++++++++++++++++++++++++++++++-- drivers/usb/host/ehci-timer.c | 34 ++++++++++++++++++++++++++++++- drivers/usb/host/ehci.h | 3 +++ 6 files changed, 84 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 387cedf2b698..7ad9ef8aedee 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -487,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd) ehci->periodic_size = DEFAULT_I_TDPS; INIT_LIST_HEAD(&ehci->async_unlink); INIT_LIST_HEAD(&ehci->async_idle); + INIT_LIST_HEAD(&ehci->intr_unlink_wait); INIT_LIST_HEAD(&ehci->intr_unlink); INIT_LIST_HEAD(&ehci->intr_qh_list); INIT_LIST_HEAD(&ehci->cached_itd_list); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 6e69ee1a3371..269a2e8ad0ab 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -345,6 +345,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) end_unlink_async(ehci); unlink_empty_async_suspended(ehci); + ehci_handle_start_intr_unlinks(ehci); ehci_handle_intr_unlinks(ehci); end_free_itds(ehci); diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index ef2c3a1eca4b..52a77734a225 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -93,6 +93,7 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) qh->qh_dma = dma; // INIT_LIST_HEAD (&qh->qh_list); INIT_LIST_HEAD (&qh->qtd_list); + INIT_LIST_HEAD(&qh->unlink_node); /* dummy td enables safe urb queuing */ qh->dummy = ehci_qtd_alloc (ehci, flags); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index f80d0330d548..94388738a6f7 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -601,12 +601,29 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) list_del(&qh->intr_node); } +static void cancel_unlink_wait_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + if (qh->qh_state != QH_STATE_LINKED || + list_empty(&qh->unlink_node)) + return; + + list_del_init(&qh->unlink_node); + + /* + * TODO: disable the event of EHCI_HRTIMER_START_UNLINK_INTR for + * avoiding unnecessary CPU wakeup + */ +} + static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) { /* If the QH isn't linked then there's nothing we can do. */ if (qh->qh_state != QH_STATE_LINKED) return; + /* if the qh is waiting for unlink, cancel it now */ + cancel_unlink_wait_intr(ehci, qh); + qh_unlink_periodic (ehci, qh); /* Make sure the unlinks are visible before starting the timer */ @@ -632,6 +649,27 @@ static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) } } +/* + * It is common only one intr URB is scheduled on one qh, and + * given complete() is run in tasklet context, introduce a bit + * delay to avoid unlink qh too early. + */ +static void start_unlink_intr_wait(struct ehci_hcd *ehci, + struct ehci_qh *qh) +{ + qh->unlink_cycle = ehci->intr_unlink_wait_cycle; + + /* New entries go at the end of the intr_unlink_wait list */ + list_add_tail(&qh->unlink_node, &ehci->intr_unlink_wait); + + if (ehci->rh_state < EHCI_RH_RUNNING) + ehci_handle_start_intr_unlinks(ehci); + else if (ehci->intr_unlink_wait.next == &qh->unlink_node) { + ehci_enable_event(ehci, EHCI_HRTIMER_START_UNLINK_INTR, true); + ++ehci->intr_unlink_wait_cycle; + } +} + static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) { struct ehci_qh_hw *hw = qh->hw; @@ -889,6 +927,9 @@ static int intr_submit ( if (qh->qh_state == QH_STATE_IDLE) { qh_refresh(ehci, qh); qh_link_periodic(ehci, qh); + } else { + /* cancel unlink wait for the qh */ + cancel_unlink_wait_intr(ehci, qh); } /* ... update usbfs periodic stats */ @@ -924,9 +965,11 @@ static void scan_intr(struct ehci_hcd *ehci) * in qh_unlink_periodic(). */ temp = qh_completions(ehci, qh); - if (unlikely(temp || (list_empty(&qh->qtd_list) && - qh->qh_state == QH_STATE_LINKED))) + if (unlikely(temp)) start_unlink_intr(ehci, qh); + else if (unlikely(list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED)) + start_unlink_intr_wait(ehci, qh); } } } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 11e5b32f73e9..424ac5d83714 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ + 5 * NSEC_PER_MSEC, /* EHCI_HRTIMER_START_UNLINK_INTR */ 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ @@ -215,6 +216,36 @@ static void ehci_handle_controller_death(struct ehci_hcd *ehci) /* Not in process context, so don't try to reset the controller */ } +/* start to unlink interrupt QHs */ +static void ehci_handle_start_intr_unlinks(struct ehci_hcd *ehci) +{ + bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); + + /* + * Process all the QHs on the intr_unlink list that were added + * before the current unlink cycle began. The list is in + * temporal order, so stop when we reach the first entry in the + * current cycle. But if the root hub isn't running then + * process all the QHs on the list. + */ + while (!list_empty(&ehci->intr_unlink_wait)) { + struct ehci_qh *qh; + + qh = list_first_entry(&ehci->intr_unlink_wait, + struct ehci_qh, unlink_node); + if (!stopped && (qh->unlink_cycle == + ehci->intr_unlink_wait_cycle)) + break; + list_del_init(&qh->unlink_node); + start_unlink_intr(ehci, qh); + } + + /* Handle remaining entries later */ + if (!list_empty(&ehci->intr_unlink_wait)) { + ehci_enable_event(ehci, EHCI_HRTIMER_START_UNLINK_INTR, true); + ++ehci->intr_unlink_wait_cycle; + } +} /* Handle unlinked interrupt QHs once they are gone from the hardware */ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) @@ -236,7 +267,7 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) unlink_node); if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle) break; - list_del(&qh->unlink_node); + list_del_init(&qh->unlink_node); end_unlink_intr(ehci, qh); } @@ -363,6 +394,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ + ehci_handle_start_intr_unlinks, /* EHCI_HRTIMER_START_UNLINK_INTR */ unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */ ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 64f9a08e959c..947752015d5d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -88,6 +88,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ + EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */ EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ @@ -143,7 +144,9 @@ struct ehci_hcd { /* one per controller */ unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ + struct list_head intr_unlink_wait; struct list_head intr_unlink; + unsigned intr_unlink_wait_cycle; unsigned intr_unlink_cycle; unsigned now_frame; /* frame from HC hardware */ unsigned last_iso_frame; /* last frame scanned for iso */ -- cgit v1.2.3-58-ga151 From 428aac8a81058e2303677a8fbf26670229e51d3a Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 3 Jul 2013 22:53:11 +0800 Subject: USB: EHCI: support running URB giveback in tasklet context All 4 transfer types can work well on EHCI HCD after switching to run URB giveback in tasklet context, so mark all HCD drivers to support it. Also we don't need to release ehci->lock during URB giveback any more. >From below test results on 3 machines(2 ARM and one x86), time consumed by EHCI interrupt handler droped much without performance loss. 1 test description 1.1 mass storage performance test: - run below command 10 times and compute the average performance dd if=/dev/sdN iflag=direct of=/dev/null bs=200M count=1 - two usb mass storage device: A: sandisk extreme USB 3.0 16G(used in test case 1 & case 2) B: kingston DataTraveler G2 4GB(only used in test case 2) 1.2 uvc function test: - run one simple capture program in the below link http://kernel.ubuntu.com/~ming/up/capture.c - capture format 640*480 and results in High Bandwidth mode on the uvc device: Z-Star 0x0ac8/0x3450 - on T410(x86) laptop, also use guvcview to watch video capture/playback 1.3 about test2 and test4 - both two devices involved are tested concurrently by above test items 1.4 how to compute irq time(the time consumed by ehci_irq) - use trace points of irq:irq_handler_entry and irq:irq_handler_exit 1.5 kernel 3.10.0-rc3-next-20130528 1.6 test machines Pandaboard A1: ARM CortexA9 dural core Arndale board: ARM CortexA15 dural core T410: i5 CPU 2.67GHz quad core 2 test result 2.1 test case1: single mass storage device performance test -------------------------------------------------------------------- upstream | patched perf(MB/s)+irq time(us) | perf(MB/s)+irq time(us) -------------------------------------------------------------------- Pandaboard A1: 25.280(avg:145,max:772) | 25.540(avg:14, max:75) Arndale board: 29.700(avg:33, max:129) | 29.700(avg:10, max:50) T410: 34.430(avg:17, max:154*)| 34.660(avg:12, max:155) --------------------------------------------------------------------- 2.2 test case2: two mass storage devices' performance test -------------------------------------------------------------------- upstream | patched perf(MB/s)+irq time(us) | perf(MB/s)+irq time(us) -------------------------------------------------------------------- Pandaboard A1: 15.840/15.580(avg:158,max:1216) | 16.500/16.160(avg:15,max:139) Arndale board: 17.370/16.220(avg:33 max:234) | 17.480/16.200(avg:11, max:91) T410: 21.180/19.820(avg:18 max:160) | 21.220/19.880(avg:11, max:149) --------------------------------------------------------------------- 2.3 test case3: one uvc streaming test - uvc device works well(on x86, luvcview can be used too and has same result with uvc capture) -------------------------------------------------------------------- upstream | patched irq time(us) | irq time(us) -------------------------------------------------------------------- Pandaboard A1: (avg:445, max:873) | (avg:33, max:44) Arndale board: (avg:316, max:630) | (avg:20, max:27) T410: (avg:39, max:107) | (avg:10, max:65) --------------------------------------------------------------------- 2.4 test case4: one uvc streaming plus one mass storage device test -------------------------------------------------------------------- upstream | patched perf(MB/s)+irq time(us) | perf(MB/s)+irq time(us) -------------------------------------------------------------------- Pandaboard A1: 20.340(avg:259,max:1704)| 20.390(avg:24, max:101) Arndale board: 23.460(avg:124,max:726) | 23.370(avg:15, max:52) T410: 28.520(avg:27, max:169) | 28.630(avg:13, max:160) --------------------------------------------------------------------- 2.5 test case5: read single mass storage device with small transfer - run below command 10 times and compute the average speed dd if=/dev/sdN iflag=direct of=/dev/null bs=4K count=4000 1), test device A: -------------------------------------------------------------------- upstream | patched perf(MB/s)+irq time(us) | perf(MB/s)+irq time(us) -------------------------------------------------------------------- Pandaboard A1: 6.5(avg:21, max:64) | 6.5(avg:10, max:24) Arndale board: 8.13(avg:12, max:23) | 8.06(avg:7, max:17) T410: 6.66(avg:13, max:131) | 6.84(avg:11, max:149) --------------------------------------------------------------------- 2), test device B: -------------------------------------------------------------------- upstream | patched perf(MB/s)+irq time(us) | perf(MB/s)+irq time(us) -------------------------------------------------------------------- Pandaboard A1: 5.5(avg:21,max:43) | 5.49(avg:10, max:24) Arndale board: 5.9(avg:12, max:22) | 5.9(avg:7, max:17) T410: 5.48(avg:13, max:155) | 5.48(avg:7, max:140) --------------------------------------------------------------------- * On T410, sometimes read ehci status register in ehci_irq takes more than 100us, and the problem has been reported on the link: http://marc.info/?t=137065867300001&r=1&w=2 Acked-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/ehci-grlib.c | 2 +- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-mv.c | 2 +- drivers/usb/host/ehci-octeon.c | 2 +- drivers/usb/host/ehci-pmcmsp.c | 2 +- drivers/usb/host/ehci-ppc-of.c | 2 +- drivers/usb/host/ehci-ps3.c | 2 +- drivers/usb/host/ehci-q.c | 5 ----- drivers/usb/host/ehci-sead3.c | 2 +- drivers/usb/host/ehci-sh.c | 2 +- drivers/usb/host/ehci-tilegx.c | 2 +- drivers/usb/host/ehci-w90x900.c | 2 +- drivers/usb/host/ehci-xilinx-of.c | 2 +- 14 files changed, 13 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 45eee6e9c6c8..e44f442e2fb7 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -669,7 +669,7 @@ static const struct hc_driver ehci_fsl_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY, + .flags = HCD_USB2 | HCD_MEMORY | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index 83ab51af250f..b52a66ce92e8 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -43,7 +43,7 @@ static const struct hc_driver ehci_grlib_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7ad9ef8aedee..73c72997cd4f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1166,7 +1166,7 @@ static const struct hc_driver ehci_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 35cdbd88bbbe..417c10da9450 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -96,7 +96,7 @@ static const struct hc_driver mv_ehci_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index 45cc00158412..ab0397e4d8f3 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -51,7 +51,7 @@ static const struct hc_driver ehci_octeon_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index 601e208bd782..893b707f0000 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -286,7 +286,7 @@ static const struct hc_driver ehci_msp_hc_driver = { #else .irq = ehci_irq, #endif - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 932293fa32de..6cc5567bf9c8 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -28,7 +28,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index fd983771b025..8188542ba17e 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -71,7 +71,7 @@ static const struct hc_driver ps3_ehci_hc_driver = { .product_desc = "PS3 EHCI Host Controller", .hcd_priv_size = sizeof(struct ehci_hcd), .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, .reset = ps3_ehci_hc_reset, .start = ehci_run, .stop = ehci_stop, diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index d34b399b78e2..b637a65e1e52 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -254,8 +254,6 @@ static int qtd_copy_status ( static void ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status) -__releases(ehci->lock) -__acquires(ehci->lock) { if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { /* ... update hc-wide periodic stats */ @@ -281,11 +279,8 @@ __acquires(ehci->lock) urb->actual_length, urb->transfer_buffer_length); #endif - /* complete() can reenter this HCD */ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); - spin_unlock (&ehci->lock); usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status); - spin_lock (&ehci->lock); } static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c index b2de52d39614..8a734498079b 100644 --- a/drivers/usb/host/ehci-sead3.c +++ b/drivers/usb/host/ehci-sead3.c @@ -55,7 +55,7 @@ const struct hc_driver ehci_sead3_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 93e59a13bc1f..dc899eb2b861 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -36,7 +36,7 @@ static const struct hc_driver ehci_sh_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY, + .flags = HCD_USB2 | HCD_MEMORY | HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c index cca4be90a864..67026ffbf9a8 100644 --- a/drivers/usb/host/ehci-tilegx.c +++ b/drivers/usb/host/ehci-tilegx.c @@ -61,7 +61,7 @@ static const struct hc_driver ehci_tilegx_hc_driver = { * Generic hardware linkage. */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * Basic lifecycle operations. diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index 59e0e24c753f..1c370dfbee0d 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -108,7 +108,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_USB2|HCD_MEMORY, + .flags = HCD_USB2|HCD_MEMORY|HCD_BH, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index eba962e6ebfb..95979f9f4381 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -79,7 +79,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, /* * basic lifecycle operations -- cgit v1.2.3-58-ga151 From bcc48f1a7a0d40ae2e5a26aff72c2b674fd8b596 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 8 Aug 2013 21:48:22 +0800 Subject: USB: introduce usb_device_no_sg_constraint() helper Some host controllers(such as xHCI) can support building packet from discontinuous buffers, so introduce one flag and helper for this kind of host controllers, then the feature can help some applications(such as usbnet) by supporting arbitrary length of sg buffers. Acked-by: Alan Stern Signed-off-by: Ming Lei Reviewed-by: Eric Dumazet Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 3 ++- include/linux/usb.h | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 4e7a311ff6d7..c12bc790a6a7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -416,7 +416,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) urb->iso_frame_desc[n].status = -EXDEV; urb->iso_frame_desc[n].actual_length = 0; } - } else if (dev->speed != USB_SPEED_WIRELESS && urb->num_sgs) { + } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint && + dev->speed != USB_SPEED_WIRELESS) { struct scatterlist *sg; int i; diff --git a/include/linux/usb.h b/include/linux/usb.h index acb1b0aa77d8..001629cd1a97 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -337,6 +337,7 @@ struct usb_bus { * the ep queue on a short transfer * with the URB_SHORT_NOT_OK flag set. */ + unsigned no_sg_constraint:1; /* no sg constraint */ unsigned sg_tablesize; /* 0 or largest number of sg list entries */ int devnum_next; /* Next open device number in @@ -684,6 +685,11 @@ static inline bool usb_device_supports_ltm(struct usb_device *udev) return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; } +static inline bool usb_device_no_sg_constraint(struct usb_device *udev) +{ + return udev && udev->bus && udev->bus->no_sg_constraint; +} + /*-------------------------------------------------------------------------*/ @@ -1253,7 +1259,7 @@ typedef void (*usb_complete_t)(struct urb *); * transfer_buffer. * @sg: scatter gather buffer list, the buffer size of each element in * the list (except the last) must be divisible by the endpoint's - * max packet size + * max packet size if no_sg_constraint isn't set in 'struct usb_bus' * @num_mapped_sgs: (internal) number of mapped sg entries * @num_sgs: number of entries in the sg list * @transfer_buffer_length: How big is transfer_buffer. The transfer may -- cgit v1.2.3-58-ga151 From fc76051c453b4e65e736aed10e57192adcbcd453 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 8 Aug 2013 21:48:23 +0800 Subject: USB: XHCI: mark no_sg_constraint This patch marks all xHCI controllers as no_sg_constraint since xHCI supports building packet from discontinuous buffers. Cc: Alan Stern Acked-by: Sarah Sharp Signed-off-by: Ming Lei Reviewed-by: Eric Dumazet Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index c8d7199eefa0..246de8905db1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4841,6 +4841,10 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; + + /* support to build packet from discontinuous buffers */ + hcd->self.no_sg_constraint = 1; + /* XHCI controllers don't stop the ep queue on short packets :| */ hcd->self.no_stop_on_short = 1; -- cgit v1.2.3-58-ga151 From eff196ad4e6b10499d4c4e4a0bd4a7c8afb8455f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 12 Aug 2013 14:01:14 -0500 Subject: usb: musb: dsps: make it depend on OF_IRQ musb_dsps.c utilizes a symbol which is only available when CONFIG_OF_IRQ is set, so make it depend on that. Signed-off-by: Felipe Balbi --- drivers/usb/musb/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 04658d7666e9..c64ee09a7c0e 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -84,6 +84,7 @@ config USB_MUSB_AM35X config USB_MUSB_DSPS tristate "TI DSPS platforms" select USB_MUSB_AM335X_CHILD + depends on OF_IRQ config USB_MUSB_BLACKFIN tristate "Blackfin" -- cgit v1.2.3-58-ga151 From 1f9230713af17657f7ed503a12ddd739d0f48089 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 5 Aug 2013 19:27:19 +0800 Subject: USB: serial: move the "simple" drivers into usb-serial-simple.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having to create a new driver for a "simple" usb to serial device, mush them all into one file, with a macro, so as to make it easy to add new ones. Cc: "René Bürgel" Acked-by: Wei Shuai Cc: Josh Triplett Acked-by: Frans Klaver Cc: "Wesley W. Terpstra" Cc: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/Kconfig | 84 ++++++------------------- drivers/usb/serial/Makefile | 9 +-- drivers/usb/serial/flashloader.c | 39 ------------ drivers/usb/serial/funsoft.c | 40 ------------ drivers/usb/serial/hp4x.c | 51 --------------- drivers/usb/serial/moto_modem.c | 48 -------------- drivers/usb/serial/siemens_mpi.c | 47 -------------- drivers/usb/serial/suunto.c | 41 ------------ drivers/usb/serial/usb-serial-simple.c | 110 +++++++++++++++++++++++++++++++++ drivers/usb/serial/vivopay-serial.c | 43 ------------- drivers/usb/serial/zio.c | 39 ------------ 11 files changed, 129 insertions(+), 422 deletions(-) delete mode 100644 drivers/usb/serial/flashloader.c delete mode 100644 drivers/usb/serial/funsoft.c delete mode 100644 drivers/usb/serial/hp4x.c delete mode 100644 drivers/usb/serial/moto_modem.c delete mode 100644 drivers/usb/serial/siemens_mpi.c delete mode 100644 drivers/usb/serial/suunto.c create mode 100644 drivers/usb/serial/usb-serial-simple.c delete mode 100644 drivers/usb/serial/vivopay-serial.c delete mode 100644 drivers/usb/serial/zio.c (limited to 'drivers/usb') diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 7eef9b33fde6..c454bfa22a10 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -51,6 +51,24 @@ config USB_SERIAL_GENERIC support" be compiled as a module for this driver to be used properly. +config USB_SERIAL_SIMPLE + tristate "USB Serial Simple Driver" + help + Say Y here to use the USB serial "simple" driver. This driver + handles a wide range of very simple devices, all in one + driver. Specifically, it supports: + - Suunto ANT+ USB device. + - Fundamental Software dongle. + - HP4x calculators + - a number of Motoroloa phones + - Siemens USB/MPI adapter. + - ViVOtech ViVOpay USB device. + - Infineon Modem Flashloader USB interface + - ZIO Motherboard USB serial interface + + To compile this driver as a module, choose M here: the module + will be called usb-serial-simple. + config USB_SERIAL_AIRCABLE tristate "USB AIRcable Bluetooth Dongle Driver" help @@ -158,14 +176,6 @@ config USB_SERIAL_FTDI_SIO To compile this driver as a module, choose M here: the module will be called ftdi_sio. -config USB_SERIAL_FUNSOFT - tristate "USB Fundamental Software Dongle Driver" - ---help--- - Say Y here if you want to use the Fundamental Software dongle. - - To compile this driver as a module, choose M here: the - module will be called funsoft. - config USB_SERIAL_VISOR tristate "USB Handspring Visor / Palm m50x / Sony Clie Driver" help @@ -462,15 +472,6 @@ config USB_SERIAL_MOS7840 To compile this driver as a module, choose M here: the module will be called mos7840. If unsure, choose N. -config USB_SERIAL_MOTOROLA - tristate "USB Motorola Phone modem driver" - ---help--- - Say Y here if you want to use a Motorola phone with a USB - connector as a modem link. - - To compile this driver as a module, choose M here: the - module will be called moto_modem. If unsure, choose N. - config USB_SERIAL_NAVMAN tristate "USB Navman GPS device" help @@ -525,14 +526,6 @@ config USB_SERIAL_SPCP8X5 To compile this driver as a module, choose M here: the module will be called spcp8x5. -config USB_SERIAL_HP4X - tristate "USB HP4x Calculators support" - help - Say Y here if you want to use an Hewlett-Packard 4x Calculator. - - To compile this driver as a module, choose M here: the - module will be called hp4x. - config USB_SERIAL_SAFE tristate "USB Safe Serial (Encapsulated) Driver" @@ -540,14 +533,6 @@ config USB_SERIAL_SAFE_PADDED bool "USB Secure Encapsulated Driver - Padded" depends on USB_SERIAL_SAFE -config USB_SERIAL_SIEMENS_MPI - tristate "USB Siemens MPI driver" - help - Say M here if you want to use a Siemens USB/MPI adapter. - - To compile this driver as a module, choose M here: the - module will be called siemens_mpi. - config USB_SERIAL_SIERRAWIRELESS tristate "USB Sierra Wireless Driver" help @@ -639,14 +624,6 @@ config USB_SERIAL_OPTICON To compile this driver as a module, choose M here: the module will be called opticon. -config USB_SERIAL_VIVOPAY_SERIAL - tristate "USB ViVOpay serial interface driver" - help - Say Y here if you want to use a ViVOtech ViVOpay USB device. - - To compile this driver as a module, choose M here: the - module will be called vivopay-serial. - config USB_SERIAL_XSENS_MT tristate "Xsens motion tracker serial interface driver" help @@ -659,14 +636,6 @@ config USB_SERIAL_XSENS_MT To compile this driver as a module, choose M here: the module will be called xsens_mt. -config USB_SERIAL_ZIO - tristate "ZIO Motherboard USB serial interface driver" - help - Say Y here if you want to use ZIO Motherboard. - - To compile this driver as a module, choose M here: the - module will be called zio. - config USB_SERIAL_WISHBONE tristate "USB-Wishbone adapter interface driver" help @@ -710,23 +679,6 @@ config USB_SERIAL_QT2 To compile this driver as a module, choose M here: the module will be called quatech-serial. -config USB_SERIAL_FLASHLOADER - tristate "Infineon Modem Flashloader USB interface driver" - help - Say Y here if you want to download Infineon Modem - via USB Flashloader serial driver. - - To compile this driver as a module, choose M here: the - module will be called flashloader. - -config USB_SERIAL_SUUNTO - tristate "USB Suunto ANT+ driver" - help - Say Y here if you want to use the Suunto ANT+ USB device. - - To compile this driver as a module, choose M here: the - module will be called suunto. - config USB_SERIAL_DEBUG tristate "USB Debugging Device" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index a14a870d993f..42670f0b5bc0 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -24,9 +24,7 @@ obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o obj-$(CONFIG_USB_SERIAL_F81232) += f81232.o obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o -obj-$(CONFIG_USB_SERIAL_FUNSOFT) += funsoft.o obj-$(CONFIG_USB_SERIAL_GARMIN) += garmin_gps.o -obj-$(CONFIG_USB_SERIAL_HP4X) += hp4x.o obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o obj-$(CONFIG_USB_SERIAL_IPW) += ipw.o obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o @@ -39,7 +37,6 @@ obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o obj-$(CONFIG_USB_SERIAL_METRO) += metro-usb.o obj-$(CONFIG_USB_SERIAL_MOS7720) += mos7720.o obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o -obj-$(CONFIG_USB_SERIAL_MOTOROLA) += moto_modem.o obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o obj-$(CONFIG_USB_SERIAL_OPTICON) += opticon.o @@ -50,11 +47,10 @@ obj-$(CONFIG_USB_SERIAL_QCAUX) += qcaux.o obj-$(CONFIG_USB_SERIAL_QUALCOMM) += qcserial.o obj-$(CONFIG_USB_SERIAL_QT2) += quatech2.o obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o -obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o +obj-$(CONFIG_USB_SERIAL_SIMPLE) += usb-serial-simple.o obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o obj-$(CONFIG_USB_SERIAL_SSU100) += ssu100.o -obj-$(CONFIG_USB_SERIAL_SUUNTO) += suunto.o obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o @@ -62,8 +58,5 @@ obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o -obj-$(CONFIG_USB_SERIAL_VIVOPAY_SERIAL) += vivopay-serial.o obj-$(CONFIG_USB_SERIAL_XSENS_MT) += xsens_mt.o -obj-$(CONFIG_USB_SERIAL_ZIO) += zio.o obj-$(CONFIG_USB_SERIAL_ZTE) += zte_ev.o -obj-$(CONFIG_USB_SERIAL_FLASHLOADER) += flashloader.o diff --git a/drivers/usb/serial/flashloader.c b/drivers/usb/serial/flashloader.c deleted file mode 100644 index e6f5c10e891c..000000000000 --- a/drivers/usb/serial/flashloader.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Infineon Flashloader driver - * - * Copyright (C) 2013 Wei Shuai - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x8087, 0x0716) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_serial_driver flashloader_device = { - .driver = { - .owner = THIS_MODULE, - .name = "flashloader", - }, - .id_table = id_table, - .num_ports = 1, -}; - -static struct usb_serial_driver * const serial_drivers[] = { - &flashloader_device, NULL -}; - -module_usb_serial_driver(serial_drivers, id_table); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/funsoft.c b/drivers/usb/serial/funsoft.c deleted file mode 100644 index 9362f8fd2385..000000000000 --- a/drivers/usb/serial/funsoft.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Funsoft Serial USB driver - * - * Copyright (C) 2006 Greg Kroah-Hartman - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x1404, 0xcddc) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_serial_driver funsoft_device = { - .driver = { - .owner = THIS_MODULE, - .name = "funsoft", - }, - .id_table = id_table, - .num_ports = 1, -}; - -static struct usb_serial_driver * const serial_drivers[] = { - &funsoft_device, NULL -}; - -module_usb_serial_driver(serial_drivers, id_table); - -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/hp4x.c b/drivers/usb/serial/hp4x.c deleted file mode 100644 index 2cba60d90c79..000000000000 --- a/drivers/usb/serial/hp4x.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * HP4x Calculators Serial USB driver - * - * Copyright (C) 2005 Arthur Huillet (ahuillet@users.sf.net) - * Copyright (C) 2001-2005 Greg Kroah-Hartman (greg@kroah.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * See Documentation/usb/usb-serial.txt for more information on using this - * driver - */ - -#include -#include -#include -#include -#include -#include - -#define DRIVER_DESC "HP4x (48/49) Generic Serial driver" - -#define HP_VENDOR_ID 0x03f0 -#define HP49GP_PRODUCT_ID 0x0121 - -static const struct usb_device_id id_table[] = { - { USB_DEVICE(HP_VENDOR_ID, HP49GP_PRODUCT_ID) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_serial_driver hp49gp_device = { - .driver = { - .owner = THIS_MODULE, - .name = "hp4X", - }, - .id_table = id_table, - .num_ports = 1, -}; - -static struct usb_serial_driver * const serial_drivers[] = { - &hp49gp_device, NULL -}; - -module_usb_serial_driver(serial_drivers, id_table); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/moto_modem.c b/drivers/usb/serial/moto_modem.c deleted file mode 100644 index c5ff6c7795a6..000000000000 --- a/drivers/usb/serial/moto_modem.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Motorola USB Phone driver - * - * Copyright (C) 2008 Greg Kroah-Hartman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * {sigh} - * Motorola should be using the CDC ACM USB spec, but instead - * they try to just "do their own thing"... This driver should handle a - * few phones in which a basic "dumb serial connection" is needed to be - * able to get a connection through to them. - */ - -#include -#include -#include -#include -#include -#include - -static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x05c6, 0x3197) }, /* unknown Motorola phone */ - { USB_DEVICE(0x0c44, 0x0022) }, /* unknown Mororola phone */ - { USB_DEVICE(0x22b8, 0x2a64) }, /* Motorola KRZR K1m */ - { USB_DEVICE(0x22b8, 0x2c84) }, /* Motorola VE240 phone */ - { USB_DEVICE(0x22b8, 0x2c64) }, /* Motorola V950 phone */ - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_serial_driver moto_device = { - .driver = { - .owner = THIS_MODULE, - .name = "moto-modem", - }, - .id_table = id_table, - .num_ports = 1, -}; - -static struct usb_serial_driver * const serial_drivers[] = { - &moto_device, NULL -}; - -module_usb_serial_driver(serial_drivers, id_table); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/siemens_mpi.c b/drivers/usb/serial/siemens_mpi.c deleted file mode 100644 index a76b1ae54a2a..000000000000 --- a/drivers/usb/serial/siemens_mpi.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Siemens USB-MPI Serial USB driver - * - * Copyright (C) 2005 Thomas Hergenhahn - * Copyright (C) 2005,2008 Greg Kroah-Hartman - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#define DRIVER_AUTHOR "Thomas Hergenhahn@web.de http://libnodave.sf.net" -#define DRIVER_DESC "Driver for Siemens USB/MPI adapter" - - -static const struct usb_device_id id_table[] = { - /* Vendor and product id for 6ES7-972-0CB20-0XA0 */ - { USB_DEVICE(0x908, 0x0004) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_serial_driver siemens_usb_mpi_device = { - .driver = { - .owner = THIS_MODULE, - .name = "siemens_mpi", - }, - .id_table = id_table, - .num_ports = 1, -}; - -static struct usb_serial_driver * const serial_drivers[] = { - &siemens_usb_mpi_device, NULL -}; - -module_usb_serial_driver(serial_drivers, id_table); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/suunto.c b/drivers/usb/serial/suunto.c deleted file mode 100644 index 2248e7a7d5ad..000000000000 --- a/drivers/usb/serial/suunto.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Suunto ANT+ USB Driver - * - * Copyright (C) 2013 Greg Kroah-Hartman - * Copyright (C) 2013 Linux Foundation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation only. - */ - -#include -#include -#include -#include -#include -#include -#include - -static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x0fcf, 0x1008) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_serial_driver suunto_device = { - .driver = { - .owner = THIS_MODULE, - .name = KBUILD_MODNAME, - }, - .id_table = id_table, - .num_ports = 1, -}; - -static struct usb_serial_driver * const serial_drivers[] = { - &suunto_device, - NULL, -}; - -module_usb_serial_driver(serial_drivers, id_table); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c new file mode 100644 index 000000000000..6a06131f48f4 --- /dev/null +++ b/drivers/usb/serial/usb-serial-simple.c @@ -0,0 +1,110 @@ +/* + * USB Serial "Simple" driver + * + * Copyright (C) 2001-2006,2008,2013 Greg Kroah-Hartman + * Copyright (C) 2005 Arthur Huillet (ahuillet@users.sf.net) + * Copyright (C) 2005 Thomas Hergenhahn + * Copyright (C) 2009 Outpost Embedded, LLC + * Copyright (C) 2010 Zilogic Systems + * Copyright (C) 2013 Wei Shuai + * Copyright (C) 2013 Linux Foundation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#define DEVICE(vendor, IDS) \ +static const struct usb_device_id vendor##_id_table[] = { \ + IDS(), \ + { }, \ +}; \ +static struct usb_serial_driver vendor##_device = { \ + .driver = { \ + .owner = THIS_MODULE, \ + .name = "stringify(vendor)", \ + }, \ + .id_table = vendor##_id_table, \ + .num_ports = 1, \ +}; + + +/* ZIO Motherboard USB driver */ +#define ZIO_IDS() \ + { USB_DEVICE(0x1CBE, 0x0103) } +DEVICE(zio, ZIO_IDS); + +/* Funsoft Serial USB driver */ +#define FUNSOFT_IDS() \ + { USB_DEVICE(0x1404, 0xcddc) } +DEVICE(funsoft, FUNSOFT_IDS); + +/* Infineon Flashloader driver */ +#define FLASHLOADER_IDS() \ + { USB_DEVICE(0x8087, 0x0716) } +DEVICE(flashloader, FLASHLOADER_IDS); + +/* ViVOpay USB Serial Driver */ +#define VIVOPAY_IDS() \ + { USB_DEVICE(0x1d5f, 0x1004) } /* ViVOpay 8800 */ +DEVICE(vivopay, VIVOPAY_IDS); + +/* Motorola USB Phone driver */ +#define MOTO_IDS() \ + { USB_DEVICE(0x05c6, 0x3197) }, /* unknown Motorola phone */ \ + { USB_DEVICE(0x0c44, 0x0022) }, /* unknown Mororola phone */ \ + { USB_DEVICE(0x22b8, 0x2a64) }, /* Motorola KRZR K1m */ \ + { USB_DEVICE(0x22b8, 0x2c84) }, /* Motorola VE240 phone */ \ + { USB_DEVICE(0x22b8, 0x2c64) } /* Motorola V950 phone */ +DEVICE(moto_modem, MOTO_IDS); + +/* HP4x (48/49) Generic Serial driver */ +#define HP4X_IDS() \ + { USB_DEVICE(0x03f0, 0x0121) } +DEVICE(hp4x, HP4X_IDS); + +/* Suunto ANT+ USB Driver */ +#define SUUNTO_IDS() \ + { USB_DEVICE(0x0fcf, 0x1008) } +DEVICE(suunto, SUUNTO_IDS); + +/* Siemens USB/MPI adapter */ +#define SIEMENS_IDS() \ + { USB_DEVICE(0x908, 0x0004) } +DEVICE(siemens_mpi, SIEMENS_IDS); + +/* All of the above structures mushed into two lists */ +static struct usb_serial_driver * const serial_drivers[] = { + &zio_device, + &funsoft_device, + &flashloader_device, + &vivopay_device, + &moto_modem_device, + &hp4x_device, + &suunto_device, + &siemens_mpi_device, + NULL +}; + +static const struct usb_device_id id_table[] = { + ZIO_IDS(), + FUNSOFT_IDS(), + FLASHLOADER_IDS(), + VIVOPAY_IDS(), + MOTO_IDS(), + HP4X_IDS(), + SUUNTO_IDS(), + SIEMENS_IDS(), + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +module_usb_serial_driver(serial_drivers, id_table); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/vivopay-serial.c b/drivers/usb/serial/vivopay-serial.c deleted file mode 100644 index 6299526ff8c3..000000000000 --- a/drivers/usb/serial/vivopay-serial.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2001-2005 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2009 Outpost Embedded, LLC - */ - -#include -#include -#include -#include -#include -#include - -#define DRIVER_DESC "ViVOpay USB Serial Driver" - -#define VIVOPAY_VENDOR_ID 0x1d5f - - -static struct usb_device_id id_table [] = { - /* ViVOpay 8800 */ - { USB_DEVICE(VIVOPAY_VENDOR_ID, 0x1004) }, - { }, -}; - -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_serial_driver vivopay_serial_device = { - .driver = { - .owner = THIS_MODULE, - .name = "vivopay-serial", - }, - .id_table = id_table, - .num_ports = 1, -}; - -static struct usb_serial_driver * const serial_drivers[] = { - &vivopay_serial_device, NULL -}; - -module_usb_serial_driver(serial_drivers, id_table); - -MODULE_AUTHOR("Forest Bond "); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/zio.c b/drivers/usb/serial/zio.c deleted file mode 100644 index c043aa84a0ba..000000000000 --- a/drivers/usb/serial/zio.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * ZIO Motherboard USB driver - * - * Copyright (C) 2010 Zilogic Systems - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x1CBE, 0x0103) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_serial_driver zio_device = { - .driver = { - .owner = THIS_MODULE, - .name = "zio", - }, - .id_table = id_table, - .num_ports = 1, -}; - -static struct usb_serial_driver * const serial_drivers[] = { - &zio_device, NULL -}; - -module_usb_serial_driver(serial_drivers, id_table); -MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From 42416cc81f4990ad8d425b41a0fc8cd985eb5fa5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 7 Aug 2013 20:28:24 +0100 Subject: usb: misc: Fix swapped properties in usb3503 DT parsing The intn and connect GPIO properties are swapped in the code which will cause failures at runtime if these are connected, fix the code. There are currently no in-tree users of this device to check or update. Signed-off-by: Mark Brown Acked-by: Olof Johansson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index cbb6b7841653..4e3a2d2fc450 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -215,10 +215,10 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } } - hub->gpio_intn = of_get_named_gpio(np, "connect-gpios", 0); + hub->gpio_intn = of_get_named_gpio(np, "intn-gpios", 0); if (hub->gpio_intn == -EPROBE_DEFER) return -EPROBE_DEFER; - hub->gpio_connect = of_get_named_gpio(np, "intn-gpios", 0); + hub->gpio_connect = of_get_named_gpio(np, "connect-gpios", 0); if (hub->gpio_connect == -EPROBE_DEFER) return -EPROBE_DEFER; hub->gpio_reset = of_get_named_gpio(np, "reset-gpios", 0); -- cgit v1.2.3-58-ga151 From cffedd6794bb32c4fbc03600fac61beca7950f08 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 7 Aug 2013 22:02:54 +0100 Subject: usb: misc: usb3503: Convert to devm_ APIs Saves us a bit of code. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 42 +++++++----------------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 4e3a2d2fc450..41b422824c87 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -187,7 +187,7 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) const u32 *property; int len; - hub = kzalloc(sizeof(struct usb3503), GFP_KERNEL); + hub = devm_kzalloc(&i2c->dev, sizeof(struct usb3503), GFP_KERNEL); if (!hub) { dev_err(&i2c->dev, "private data alloc fail\n"); return err; @@ -229,35 +229,35 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } if (gpio_is_valid(hub->gpio_intn)) { - err = gpio_request_one(hub->gpio_intn, + err = devm_gpio_request_one(&i2c->dev, hub->gpio_intn, GPIOF_OUT_INIT_HIGH, "usb3503 intn"); if (err) { dev_err(&i2c->dev, "unable to request GPIO %d as connect pin (%d)\n", hub->gpio_intn, err); - goto err_out; + return err; } } if (gpio_is_valid(hub->gpio_connect)) { - err = gpio_request_one(hub->gpio_connect, + err = devm_gpio_request_one(&i2c->dev, hub->gpio_connect, GPIOF_OUT_INIT_HIGH, "usb3503 connect"); if (err) { dev_err(&i2c->dev, "unable to request GPIO %d as connect pin (%d)\n", hub->gpio_connect, err); - goto err_gpio_connect; + return err; } } if (gpio_is_valid(hub->gpio_reset)) { - err = gpio_request_one(hub->gpio_reset, + err = devm_gpio_request_one(&i2c->dev, hub->gpio_reset, GPIOF_OUT_INIT_LOW, "usb3503 reset"); if (err) { dev_err(&i2c->dev, "unable to request GPIO %d as reset pin (%d)\n", hub->gpio_reset, err); - goto err_gpio_reset; + return err; } } @@ -267,33 +267,6 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) (hub->mode == USB3503_MODE_HUB) ? "hub" : "standby"); return 0; - -err_gpio_reset: - if (gpio_is_valid(hub->gpio_connect)) - gpio_free(hub->gpio_connect); -err_gpio_connect: - if (gpio_is_valid(hub->gpio_intn)) - gpio_free(hub->gpio_intn); -err_out: - kfree(hub); - - return err; -} - -static int usb3503_remove(struct i2c_client *i2c) -{ - struct usb3503 *hub = i2c_get_clientdata(i2c); - - if (gpio_is_valid(hub->gpio_intn)) - gpio_free(hub->gpio_intn); - if (gpio_is_valid(hub->gpio_connect)) - gpio_free(hub->gpio_connect); - if (gpio_is_valid(hub->gpio_reset)) - gpio_free(hub->gpio_reset); - - kfree(hub); - - return 0; } static const struct i2c_device_id usb3503_id[] = { @@ -316,7 +289,6 @@ static struct i2c_driver usb3503_driver = { .of_match_table = of_match_ptr(usb3503_of_match), }, .probe = usb3503_probe, - .remove = usb3503_remove, .id_table = usb3503_id, }; -- cgit v1.2.3-58-ga151 From 900e06212df8bc20bcdcf3b71b4e2909287ce257 Mon Sep 17 00:00:00 2001 From: David Daney Date: Fri, 9 Aug 2013 18:17:48 -0700 Subject: usb: Move definition of USB_EHCI_BIG_ENDIAN_MMIO et al. out side of the ifs. When CONFIG_USB_SUPPORT is not selected we get things like: scripts/kconfig/mconf Kconfig warning: (MIPS_SEAD3 && PMC_MSP && CPU_CAVIUM_OCTEON) selects USB_EHCI_BIG_ENDIAN_MMIO which has unmet direct dependencies (USB_SUPPORT && USB) It is much cleaner to make the various system Kconfigs select USB_EHCI_BIG_ENDIAN_MMIO rather than move the system config information into USB's Kconfig, but the warnings are annoying. Eliminate the warning by moving the definition of USB_EHCI_BIG_ENDIAN_MMIO outside of all the Kconfig if statements. While we are at it move USB_OHCI_BIG_ENDIAN_DESC, USB_OHCI_BIG_ENDIAN_MMIO, USB_OHCI_LITTLE_ENDIAN and USB_EHCI_BIG_ENDIAN_DESC too, as they could very well suffer similar problems for other systems. Get rid of the redundant "default n" in USB_OHCI_BIG_ENDIAN_DESC and USB_OHCI_BIG_ENDIAN_MMIO Signed-off-by: David Daney Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 17 +++++++++++++++++ drivers/usb/host/Kconfig | 20 -------------------- 2 files changed, 17 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 73f62caa8609..2642b8a11e05 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -6,9 +6,26 @@ config USB_ARCH_HAS_OHCI bool +config USB_OHCI_BIG_ENDIAN_DESC + bool + +config USB_OHCI_BIG_ENDIAN_MMIO + bool + +config USB_OHCI_LITTLE_ENDIAN + bool + default n if STB03xxx || PPC_MPC52xx + default y + config USB_ARCH_HAS_EHCI bool +config USB_EHCI_BIG_ENDIAN_MMIO + bool + +config USB_EHCI_BIG_ENDIAN_DESC + bool + config USB_ARCH_HAS_XHCI bool diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index cf521d6551dd..0947aac857a2 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -113,12 +113,6 @@ config USB_EHCI_HCD_PMC_MSP Enables support for the onchip USB controller on the PMC_MSP7100 Family SoC's. If unsure, say N. -config USB_EHCI_BIG_ENDIAN_MMIO - bool - -config USB_EHCI_BIG_ENDIAN_DESC - bool - config XPS_USB_HCD_XILINX bool "Use Xilinx usb host EHCI controller core" depends on (PPC32 || MICROBLAZE) @@ -509,20 +503,6 @@ config USB_OCTEON_OHCI controller. It is needed for low-speed USB 1.0 device support. All CN6XXX based chips with USB are supported. - -config USB_OHCI_BIG_ENDIAN_DESC - bool - default n - -config USB_OHCI_BIG_ENDIAN_MMIO - bool - default n - -config USB_OHCI_LITTLE_ENDIAN - bool - default n if STB03xxx || PPC_MPC52xx - default y - endif # USB_OHCI_HCD config USB_UHCI_HCD -- cgit v1.2.3-58-ga151 From 24455b09b4838cdc0165af0f24ca63ec9ad54e44 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 11:41:49 +0100 Subject: usb: misc: usb3503: Use gpio_set_value_cansleep() The /RESET GPIO is not manipulated from atomic context so support GPIOs that can't be written from atomic context by using _cansleep(). Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 41b422824c87..2e9e100a182e 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -103,7 +103,7 @@ static int usb3503_clear_bits(struct i2c_client *client, char reg, char req) static int usb3503_reset(int gpio_reset, int state) { if (gpio_is_valid(gpio_reset)) - gpio_set_value(gpio_reset, state); + gpio_set_value_cansleep(gpio_reset, state); /* Wait T_HUBINIT == 4ms for hub logic to stabilize */ if (state) -- cgit v1.2.3-58-ga151 From 8e7245b8386cb1dc941e10a4c97307e3f48da5da Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 11:41:51 +0100 Subject: usb: misc: usb3503: Actively manage Hub Connect GPIO If the connect signal is pulled high then the device will start up meaning that if we just pull it high on probe then the device will start running prior to the configuration being written out. Fix this by pulling the GPIO low when we reset and only pulling it high when configuration is finished. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 2e9e100a182e..4b6572a37e87 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -100,10 +100,13 @@ static int usb3503_clear_bits(struct i2c_client *client, char reg, char req) return 0; } -static int usb3503_reset(int gpio_reset, int state) +static int usb3503_reset(struct usb3503 *hub, int state) { - if (gpio_is_valid(gpio_reset)) - gpio_set_value_cansleep(gpio_reset, state); + if (!state && gpio_is_valid(hub->gpio_connect)) + gpio_set_value_cansleep(hub->gpio_connect, 0); + + if (gpio_is_valid(hub->gpio_reset)) + gpio_set_value_cansleep(hub->gpio_reset, state); /* Wait T_HUBINIT == 4ms for hub logic to stabilize */ if (state) @@ -119,7 +122,7 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) switch (mode) { case USB3503_MODE_HUB: - usb3503_reset(hub->gpio_reset, 1); + usb3503_reset(hub, 1); /* SP_ILOCK: set connect_n, config_n for config */ err = usb3503_write_register(i2c, USB3503_SP_ILOCK, @@ -156,12 +159,15 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) goto err_hubmode; } + if (gpio_is_valid(hub->gpio_connect)) + gpio_set_value_cansleep(hub->gpio_connect, 1); + hub->mode = mode; dev_info(&i2c->dev, "switched to HUB mode\n"); break; case USB3503_MODE_STANDBY: - usb3503_reset(hub->gpio_reset, 0); + usb3503_reset(hub, 0); hub->mode = mode; dev_info(&i2c->dev, "switched to STANDBY mode\n"); @@ -241,7 +247,7 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (gpio_is_valid(hub->gpio_connect)) { err = devm_gpio_request_one(&i2c->dev, hub->gpio_connect, - GPIOF_OUT_INIT_HIGH, "usb3503 connect"); + GPIOF_OUT_INIT_LOW, "usb3503 connect"); if (err) { dev_err(&i2c->dev, "unable to request GPIO %d as connect pin (%d)\n", -- cgit v1.2.3-58-ga151 From 68b14134be55eca7340b9a8b3ec4cb8f79622a3c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 11:41:52 +0100 Subject: usb: misc: usb3503: Convert to regmap This will give access to the diagnostic infrastructure regmap has but the main point is to support future refactoring. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/Kconfig | 1 + drivers/usb/misc/usb3503.c | 93 ++++++++++++++++++---------------------------- 2 files changed, 37 insertions(+), 57 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index ca91420f5c21..e2b21c1d9c40 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -246,5 +246,6 @@ config USB_EZUSB_FX2 config USB_HSIC_USB3503 tristate "USB3503 HSIC to USB20 Driver" depends on I2C + select REGMAP help This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver. diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 4b6572a37e87..f2c0356b7148 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -26,6 +26,7 @@ #include #include #include +#include #define USB3503_VIDL 0x00 #define USB3503_VIDM 0x01 @@ -50,56 +51,18 @@ #define USB3503_CFGP 0xee #define USB3503_CLKSUSP (1 << 7) +#define USB3503_RESET 0xff + struct usb3503 { enum usb3503_mode mode; - struct i2c_client *client; + struct regmap *regmap; + struct device *dev; u8 port_off_mask; int gpio_intn; int gpio_reset; int gpio_connect; }; -static int usb3503_write_register(struct i2c_client *client, - char reg, char data) -{ - return i2c_smbus_write_byte_data(client, reg, data); -} - -static int usb3503_read_register(struct i2c_client *client, char reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int usb3503_set_bits(struct i2c_client *client, char reg, char req) -{ - int err; - - err = usb3503_read_register(client, reg); - if (err < 0) - return err; - - err = usb3503_write_register(client, reg, err | req); - if (err < 0) - return err; - - return 0; -} - -static int usb3503_clear_bits(struct i2c_client *client, char reg, char req) -{ - int err; - - err = usb3503_read_register(client, reg); - if (err < 0) - return err; - - err = usb3503_write_register(client, reg, err & ~req); - if (err < 0) - return err; - - return 0; -} - static int usb3503_reset(struct usb3503 *hub, int state) { if (!state && gpio_is_valid(hub->gpio_connect)) @@ -117,7 +80,7 @@ static int usb3503_reset(struct usb3503 *hub, int state) static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) { - struct i2c_client *i2c = hub->client; + struct device *dev = hub->dev; int err = 0; switch (mode) { @@ -125,37 +88,40 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) usb3503_reset(hub, 1); /* SP_ILOCK: set connect_n, config_n for config */ - err = usb3503_write_register(i2c, USB3503_SP_ILOCK, + err = regmap_write(hub->regmap, USB3503_SP_ILOCK, (USB3503_SPILOCK_CONNECT | USB3503_SPILOCK_CONFIG)); if (err < 0) { - dev_err(&i2c->dev, "SP_ILOCK failed (%d)\n", err); + dev_err(dev, "SP_ILOCK failed (%d)\n", err); goto err_hubmode; } /* PDS : Disable For Self Powered Operation */ if (hub->port_off_mask) { - err = usb3503_set_bits(i2c, USB3503_PDS, + err = regmap_update_bits(hub->regmap, USB3503_PDS, + hub->port_off_mask, hub->port_off_mask); if (err < 0) { - dev_err(&i2c->dev, "PDS failed (%d)\n", err); + dev_err(dev, "PDS failed (%d)\n", err); goto err_hubmode; } } /* CFG1 : SELF_BUS_PWR -> Self-Powerd operation */ - err = usb3503_set_bits(i2c, USB3503_CFG1, USB3503_SELF_BUS_PWR); + err = regmap_update_bits(hub->regmap, USB3503_CFG1, + USB3503_SELF_BUS_PWR, + USB3503_SELF_BUS_PWR); if (err < 0) { - dev_err(&i2c->dev, "CFG1 failed (%d)\n", err); + dev_err(dev, "CFG1 failed (%d)\n", err); goto err_hubmode; } /* SP_LOCK: clear connect_n, config_n for hub connect */ - err = usb3503_clear_bits(i2c, USB3503_SP_ILOCK, - (USB3503_SPILOCK_CONNECT - | USB3503_SPILOCK_CONFIG)); + err = regmap_update_bits(hub->regmap, USB3503_SP_ILOCK, + (USB3503_SPILOCK_CONNECT + | USB3503_SPILOCK_CONFIG), 0); if (err < 0) { - dev_err(&i2c->dev, "SP_ILOCK failed (%d)\n", err); + dev_err(dev, "SP_ILOCK failed (%d)\n", err); goto err_hubmode; } @@ -163,18 +129,18 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) gpio_set_value_cansleep(hub->gpio_connect, 1); hub->mode = mode; - dev_info(&i2c->dev, "switched to HUB mode\n"); + dev_info(dev, "switched to HUB mode\n"); break; case USB3503_MODE_STANDBY: usb3503_reset(hub, 0); hub->mode = mode; - dev_info(&i2c->dev, "switched to STANDBY mode\n"); + dev_info(dev, "switched to STANDBY mode\n"); break; default: - dev_err(&i2c->dev, "unknown mode is request\n"); + dev_err(dev, "unknown mode is request\n"); err = -EINVAL; break; } @@ -183,6 +149,13 @@ err_hubmode: return err; } +static const struct regmap_config usb3503_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = USB3503_RESET, +}; + static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct usb3503_platform_data *pdata = dev_get_platdata(&i2c->dev); @@ -200,7 +173,13 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } i2c_set_clientdata(i2c, hub); - hub->client = i2c; + hub->regmap = devm_regmap_init_i2c(i2c, &usb3503_regmap_config); + if (IS_ERR(hub->regmap)) { + err = PTR_ERR(hub->regmap); + dev_err(&i2c->dev, "Failed to initialise regmap: %d\n", err); + return err; + } + hub->dev = &i2c->dev; if (pdata) { hub->port_off_mask = pdata->port_off_mask; -- cgit v1.2.3-58-ga151 From 2487e3ee33dd6c4fa3dabbe11bc988883be81f1e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 11:41:53 +0100 Subject: usb: misc: usb3503: Factor out I2C probe In preparation for supporting operation without an I2C control interface factor out the I2C-specific parts of the probe routine from those that don't do any register I/O. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 77 ++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 34 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index f2c0356b7148..ca0f789d78a3 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -156,31 +156,16 @@ static const struct regmap_config usb3503_regmap_config = { .max_register = USB3503_RESET, }; -static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +static int usb3503_probe(struct usb3503 *hub) { - struct usb3503_platform_data *pdata = dev_get_platdata(&i2c->dev); - struct device_node *np = i2c->dev.of_node; - struct usb3503 *hub; - int err = -ENOMEM; + struct device *dev = hub->dev; + struct usb3503_platform_data *pdata = dev_get_platdata(dev); + struct device_node *np = dev->of_node; + int err; u32 mode = USB3503_MODE_UNKNOWN; const u32 *property; int len; - hub = devm_kzalloc(&i2c->dev, sizeof(struct usb3503), GFP_KERNEL); - if (!hub) { - dev_err(&i2c->dev, "private data alloc fail\n"); - return err; - } - - i2c_set_clientdata(i2c, hub); - hub->regmap = devm_regmap_init_i2c(i2c, &usb3503_regmap_config); - if (IS_ERR(hub->regmap)) { - err = PTR_ERR(hub->regmap); - dev_err(&i2c->dev, "Failed to initialise regmap: %d\n", err); - return err; - } - hub->dev = &i2c->dev; - if (pdata) { hub->port_off_mask = pdata->port_off_mask; hub->gpio_intn = pdata->gpio_intn; @@ -214,46 +199,70 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } if (gpio_is_valid(hub->gpio_intn)) { - err = devm_gpio_request_one(&i2c->dev, hub->gpio_intn, + err = devm_gpio_request_one(dev, hub->gpio_intn, GPIOF_OUT_INIT_HIGH, "usb3503 intn"); if (err) { - dev_err(&i2c->dev, - "unable to request GPIO %d as connect pin (%d)\n", - hub->gpio_intn, err); + dev_err(dev, + "unable to request GPIO %d as connect pin (%d)\n", + hub->gpio_intn, err); return err; } } if (gpio_is_valid(hub->gpio_connect)) { - err = devm_gpio_request_one(&i2c->dev, hub->gpio_connect, + err = devm_gpio_request_one(dev, hub->gpio_connect, GPIOF_OUT_INIT_LOW, "usb3503 connect"); if (err) { - dev_err(&i2c->dev, - "unable to request GPIO %d as connect pin (%d)\n", - hub->gpio_connect, err); + dev_err(dev, + "unable to request GPIO %d as connect pin (%d)\n", + hub->gpio_connect, err); return err; } } if (gpio_is_valid(hub->gpio_reset)) { - err = devm_gpio_request_one(&i2c->dev, hub->gpio_reset, + err = devm_gpio_request_one(dev, hub->gpio_reset, GPIOF_OUT_INIT_LOW, "usb3503 reset"); if (err) { - dev_err(&i2c->dev, - "unable to request GPIO %d as reset pin (%d)\n", - hub->gpio_reset, err); + dev_err(dev, + "unable to request GPIO %d as reset pin (%d)\n", + hub->gpio_reset, err); return err; } } usb3503_switch_mode(hub, hub->mode); - dev_info(&i2c->dev, "%s: probed on %s mode\n", __func__, + dev_info(dev, "%s: probed on %s mode\n", __func__, (hub->mode == USB3503_MODE_HUB) ? "hub" : "standby"); return 0; } +static int usb3503_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct usb3503 *hub; + int err; + + hub = devm_kzalloc(&i2c->dev, sizeof(struct usb3503), GFP_KERNEL); + if (!hub) { + dev_err(&i2c->dev, "private data alloc fail\n"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, hub); + hub->regmap = devm_regmap_init_i2c(i2c, &usb3503_regmap_config); + if (IS_ERR(hub->regmap)) { + err = PTR_ERR(hub->regmap); + dev_err(&i2c->dev, "Failed to initialise regmap: %d\n", err); + return err; + } + hub->dev = &i2c->dev; + + return usb3503_probe(hub); +} + static const struct i2c_device_id usb3503_id[] = { { USB3503_I2C_NAME, 0 }, { } @@ -273,7 +282,7 @@ static struct i2c_driver usb3503_driver = { .name = USB3503_I2C_NAME, .of_match_table = of_match_ptr(usb3503_of_match), }, - .probe = usb3503_probe, + .probe = usb3503_i2c_probe, .id_table = usb3503_id, }; -- cgit v1.2.3-58-ga151 From dd8e670d2775c02169a3b9e4c8d84c8652342836 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 11:41:54 +0100 Subject: usb: misc: usb3503: Fix typos in error messages Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index ca0f789d78a3..777102e05c3b 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -140,7 +140,7 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) break; default: - dev_err(dev, "unknown mode is request\n"); + dev_err(dev, "unknown mode is requested\n"); err = -EINVAL; break; } @@ -233,7 +233,7 @@ static int usb3503_probe(struct usb3503 *hub) usb3503_switch_mode(hub, hub->mode); - dev_info(dev, "%s: probed on %s mode\n", __func__, + dev_info(dev, "%s: probed in %s mode\n", __func__, (hub->mode == USB3503_MODE_HUB) ? "hub" : "standby"); return 0; -- cgit v1.2.3-58-ga151 From e5a0c874ec7babc1931a67489b292de152d2a641 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 11:41:55 +0100 Subject: usb: misc: usb3503: Default to hub mode Since there is no runtime interface for changing modes this is probably the most sensible default. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 777102e05c3b..8f5dff20dcb4 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -162,7 +162,7 @@ static int usb3503_probe(struct usb3503 *hub) struct usb3503_platform_data *pdata = dev_get_platdata(dev); struct device_node *np = dev->of_node; int err; - u32 mode = USB3503_MODE_UNKNOWN; + u32 mode = USB3503_MODE_HUB; const u32 *property; int len; -- cgit v1.2.3-58-ga151 From 5bdd1f4a1daf398042203b72430891dfc40c0fa6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 11:41:56 +0100 Subject: usb: misc: usb3503: Add USB3503A to the compatible list There are no software visible differences that I am aware of but in case any are discovered allow the DTS to specify exactly which device is present. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb3503.txt | 2 +- drivers/usb/misc/usb3503.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/Documentation/devicetree/bindings/usb/usb3503.txt b/Documentation/devicetree/bindings/usb/usb3503.txt index 8c5be48b43c8..cd60f7e4eb57 100644 --- a/Documentation/devicetree/bindings/usb/usb3503.txt +++ b/Documentation/devicetree/bindings/usb/usb3503.txt @@ -1,7 +1,7 @@ SMSC USB3503 High-Speed Hub Controller Required properties: -- compatible: Should be "smsc,usb3503". +- compatible: Should be "smsc,usb3503" or "smsc,usb3503a". - reg: Specifies the i2c slave address, it should be 0x08. - connect-gpios: Should specify GPIO for connect. - disabled-ports: Should specify the ports unused. diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 8f5dff20dcb4..da45ed971332 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -272,6 +272,7 @@ MODULE_DEVICE_TABLE(i2c, usb3503_id); #ifdef CONFIG_OF static const struct of_device_id usb3503_of_match[] = { { .compatible = "smsc,usb3503", }, + { .compatible = "smsc,usb3503a", }, {}, }; MODULE_DEVICE_TABLE(of, usb3503_of_match); -- cgit v1.2.3-58-ga151 From 3f0d1c67fa20d524fdcb4a7dcda19ed76c59be44 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 11:41:58 +0100 Subject: usb: misc: usb3503: Support operation with no I2C control Refactor so that register writes for configuration are only performed if the device has a regmap provided and also register as a platform driver. This allows the driver to be used to manage GPIO based control of the device. Signed-off-by: Mark Brown Cc: devicetree@vger.kernel.org Reviewed-by: Dongjin Kim Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb3503.txt | 3 +- drivers/usb/misc/usb3503.c | 93 ++++++++++++++++++----- 2 files changed, 78 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/Documentation/devicetree/bindings/usb/usb3503.txt b/Documentation/devicetree/bindings/usb/usb3503.txt index 44a03f338424..a018da4a7ad7 100644 --- a/Documentation/devicetree/bindings/usb/usb3503.txt +++ b/Documentation/devicetree/bindings/usb/usb3503.txt @@ -2,9 +2,10 @@ SMSC USB3503 High-Speed Hub Controller Required properties: - compatible: Should be "smsc,usb3503" or "smsc,usb3503a". -- reg: Specifies the i2c slave address, it should be 0x08. Optional properties: +- reg: Specifies the i2c slave address, it is required and should be 0x08 + if I2C is used. - connect-gpios: Should specify GPIO for connect. - disabled-ports: Should specify the ports unused. '1' or '2' or '3' are availe for this property to describe the port diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index da45ed971332..a31641e18d19 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -78,22 +78,21 @@ static int usb3503_reset(struct usb3503 *hub, int state) return 0; } -static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) +static int usb3503_connect(struct usb3503 *hub) { struct device *dev = hub->dev; - int err = 0; + int err; - switch (mode) { - case USB3503_MODE_HUB: - usb3503_reset(hub, 1); + usb3503_reset(hub, 1); + if (hub->regmap) { /* SP_ILOCK: set connect_n, config_n for config */ err = regmap_write(hub->regmap, USB3503_SP_ILOCK, - (USB3503_SPILOCK_CONNECT + (USB3503_SPILOCK_CONNECT | USB3503_SPILOCK_CONFIG)); if (err < 0) { dev_err(dev, "SP_ILOCK failed (%d)\n", err); - goto err_hubmode; + return err; } /* PDS : Disable For Self Powered Operation */ @@ -103,7 +102,7 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) hub->port_off_mask); if (err < 0) { dev_err(dev, "PDS failed (%d)\n", err); - goto err_hubmode; + return err; } } @@ -113,7 +112,7 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) USB3503_SELF_BUS_PWR); if (err < 0) { dev_err(dev, "CFG1 failed (%d)\n", err); - goto err_hubmode; + return err; } /* SP_LOCK: clear connect_n, config_n for hub connect */ @@ -122,14 +121,27 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) | USB3503_SPILOCK_CONFIG), 0); if (err < 0) { dev_err(dev, "SP_ILOCK failed (%d)\n", err); - goto err_hubmode; + return err; } + } - if (gpio_is_valid(hub->gpio_connect)) - gpio_set_value_cansleep(hub->gpio_connect, 1); + if (gpio_is_valid(hub->gpio_connect)) + gpio_set_value_cansleep(hub->gpio_connect, 1); - hub->mode = mode; - dev_info(dev, "switched to HUB mode\n"); + hub->mode = USB3503_MODE_HUB; + dev_info(dev, "switched to HUB mode\n"); + + return 0; +} + +static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) +{ + struct device *dev = hub->dev; + int err = 0; + + switch (mode) { + case USB3503_MODE_HUB: + err = usb3503_connect(hub); break; case USB3503_MODE_STANDBY: @@ -145,7 +157,6 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) break; } -err_hubmode: return err; } @@ -198,6 +209,9 @@ static int usb3503_probe(struct usb3503 *hub) hub->mode = mode; } + if (hub->port_off_mask && !hub->regmap) + dev_err(dev, "Ports disabled with no control interface\n"); + if (gpio_is_valid(hub->gpio_intn)) { err = devm_gpio_request_one(dev, hub->gpio_intn, GPIOF_OUT_INIT_HIGH, "usb3503 intn"); @@ -263,6 +277,20 @@ static int usb3503_i2c_probe(struct i2c_client *i2c, return usb3503_probe(hub); } +static int usb3503_platform_probe(struct platform_device *pdev) +{ + struct usb3503 *hub; + + hub = devm_kzalloc(&pdev->dev, sizeof(struct usb3503), GFP_KERNEL); + if (!hub) { + dev_err(&pdev->dev, "private data alloc fail\n"); + return -ENOMEM; + } + hub->dev = &pdev->dev; + + return usb3503_probe(hub); +} + static const struct i2c_device_id usb3503_id[] = { { USB3503_I2C_NAME, 0 }, { } @@ -278,7 +306,7 @@ static const struct of_device_id usb3503_of_match[] = { MODULE_DEVICE_TABLE(of, usb3503_of_match); #endif -static struct i2c_driver usb3503_driver = { +static struct i2c_driver usb3503_i2c_driver = { .driver = { .name = USB3503_I2C_NAME, .of_match_table = of_match_ptr(usb3503_of_match), @@ -287,7 +315,38 @@ static struct i2c_driver usb3503_driver = { .id_table = usb3503_id, }; -module_i2c_driver(usb3503_driver); +static struct platform_driver usb3503_platform_driver = { + .driver = { + .name = USB3503_I2C_NAME, + .of_match_table = of_match_ptr(usb3503_of_match), + .owner = THIS_MODULE, + }, + .probe = usb3503_platform_probe, +}; + +static int __init usb3503_init(void) +{ + int err; + + err = i2c_register_driver(THIS_MODULE, &usb3503_i2c_driver); + if (err != 0) + pr_err("usb3503: Failed to register I2C driver: %d\n", err); + + err = platform_driver_register(&usb3503_platform_driver); + if (err != 0) + pr_err("usb3503: Failed to register platform driver: %d\n", + err); + + return 0; +} +module_init(usb3503_init); + +static void __exit usb3503_exit(void) +{ + platform_driver_unregister(&usb3503_platform_driver); + i2c_del_driver(&usb3503_i2c_driver); +} +module_exit(usb3503_exit); MODULE_AUTHOR("Dongjin Kim "); MODULE_DESCRIPTION("USB3503 USB HUB driver"); -- cgit v1.2.3-58-ga151 From b4f17a488ae2e09bfcf95c0e0b4219c246f1116a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 3 Aug 2013 16:37:48 +0200 Subject: usb: config->desc.bLength may not exceed amount of data returned by the device While reading the config parsing code I noticed this check is missing, without this check config->desc.wTotalLength can end up with a value larger then the dev->rawdescriptors length for the config, and when userspace then tries to get the rawdescriptors bad things may happen. Signed-off-by: Hans de Goede Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 7199adccf444..a6b2cabe7930 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -424,7 +424,8 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); if (config->desc.bDescriptorType != USB_DT_CONFIG || - config->desc.bLength < USB_DT_CONFIG_SIZE) { + config->desc.bLength < USB_DT_CONFIG_SIZE || + config->desc.bLength > size) { dev_err(ddev, "invalid descriptor for config index %d: " "type = 0x%X, length = %d\n", cfgidx, config->desc.bDescriptorType, config->desc.bLength); -- cgit v1.2.3-58-ga151 From c10750b2bee7677fe76c97c53e079937802dc68c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 7 Aug 2013 22:32:40 +0100 Subject: usb/hcd: Log error code if reset() fails If someone provided meaningful error codes from reset() we should tell the user what they were. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 0ffe016f4036..5aa1d133eaf9 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2653,7 +2653,7 @@ int usb_add_hcd(struct usb_hcd *hcd, * should already have been reset (and boot firmware kicked off etc). */ if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { - dev_err(hcd->self.controller, "can't setup\n"); + dev_err(hcd->self.controller, "can't setup: %d\n", retval); goto err_hcd_driver_setup; } hcd->rh_pollable = 1; -- cgit v1.2.3-58-ga151 From 9841f37a1cca5357c1fd198b1068c12955aa632f Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Thu, 8 Aug 2013 16:49:24 -0700 Subject: usb: ehci: Add support for SINGLE_STEP_SET_FEATURE test of EHSET The USB Embedded High-speed Host Electrical Test (EHSET) defines the SINGLE_STEP_SET_FEATURE test as follows: 1) The host enumerates the test device with VID:0x1A0A, PID:0x0108 2) The host sends the SETUP stage of a GetDescriptor(Device) 3) The device ACKs the request 4) The host issues SOFs for 15 seconds allowing the test operator to raise the scope trigger just above the SOF voltage level 5) The host sends the IN packet 6) The device sends data in response, triggering the scope 7) The host sends an ACK in response to the data This patch adds additional handling to the EHCI hub driver and allows the EHSET driver to initiate this test mode by issuing a a SetFeature request to the root hub with a Test Selector value of 0x06. From there it mimics ehci_urb_enqueue() but separately submits QTDs for the SETUP and DATA/STATUS stages in order to insert a delay in between. Signed-off-by: Manu Gautam Acked-by: Alan Stern [jackp@codeaurora.org: imported from commit c2084930 on codeaurora.org; minor cleanup and updated author email] Signed-off-by: Jack Pham Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 144 +++++++++++++++++++++++++++++++++++++++++++- drivers/usb/host/ehci-q.c | 101 +++++++++++++++++++++++++++++++ drivers/usb/misc/ehset.c | 20 +++++- 3 files changed, 263 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 269a2e8ad0ab..d0d4cc151e71 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -711,6 +711,142 @@ ehci_hub_descriptor ( desc->wHubCharacteristics = cpu_to_le16(temp); } +/*-------------------------------------------------------------------------*/ +#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06 + +static void usb_ehset_completion(struct urb *urb) +{ + struct completion *done = urb->context; + + complete(done); +} +static int submit_single_step_set_feature( + struct usb_hcd *hcd, + struct urb *urb, + int is_setup +); + +/* + * Allocate and initialize a control URB. This request will be used by the + * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages + * of the GetDescriptor request are sent 15 seconds after the SETUP stage. + * Return NULL if failed. + */ +static struct urb *request_single_step_set_feature_urb( + struct usb_device *udev, + void *dr, + void *buf, + struct completion *done +) { + struct urb *urb; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + struct usb_host_endpoint *ep; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return NULL; + + urb->pipe = usb_rcvctrlpipe(udev, 0); + ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (!ep) { + usb_free_urb(urb); + return NULL; + } + + urb->ep = ep; + urb->dev = udev; + urb->setup_packet = (void *)dr; + urb->transfer_buffer = buf; + urb->transfer_buffer_length = USB_DT_DEVICE_SIZE; + urb->complete = usb_ehset_completion; + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->transfer_flags = URB_DIR_IN; + usb_get_urb(urb); + atomic_inc(&urb->use_count); + atomic_inc(&urb->dev->urbnum); + urb->setup_dma = dma_map_single( + hcd->self.controller, + urb->setup_packet, + sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); + urb->transfer_dma = dma_map_single( + hcd->self.controller, + urb->transfer_buffer, + urb->transfer_buffer_length, + DMA_FROM_DEVICE); + urb->context = done; + return urb; +} + +static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port) +{ + int retval = -ENOMEM; + struct usb_ctrlrequest *dr; + struct urb *urb; + struct usb_device *udev; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct usb_device_descriptor *buf; + DECLARE_COMPLETION_ONSTACK(done); + + /* Obtain udev of the rhub's child port */ + udev = usb_hub_find_child(hcd->self.root_hub, port); + if (!udev) { + ehci_err(ehci, "No device attached to the RootHub\n"); + return -ENODEV; + } + buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!dr) { + kfree(buf); + return -ENOMEM; + } + + /* Fill Setup packet for GetDescriptor */ + dr->bRequestType = USB_DIR_IN; + dr->bRequest = USB_REQ_GET_DESCRIPTOR; + dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8); + dr->wIndex = 0; + dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE); + urb = request_single_step_set_feature_urb(udev, dr, buf, &done); + if (!urb) + goto cleanup; + + /* Submit just the SETUP stage */ + retval = submit_single_step_set_feature(hcd, urb, 1); + if (retval) + goto out1; + if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) { + usb_kill_urb(urb); + retval = -ETIMEDOUT; + ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__); + goto out1; + } + msleep(15 * 1000); + + /* Complete remaining DATA and STATUS stages using the same URB */ + urb->status = -EINPROGRESS; + usb_get_urb(urb); + atomic_inc(&urb->use_count); + atomic_inc(&urb->dev->urbnum); + retval = submit_single_step_set_feature(hcd, urb, 0); + if (!retval && !wait_for_completion_timeout(&done, + msecs_to_jiffies(2000))) { + usb_kill_urb(urb); + retval = -ETIMEDOUT; + ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__); + } +out1: + usb_free_urb(urb); +cleanup: + kfree(dr); + kfree(buf); + return retval; +} /*-------------------------------------------------------------------------*/ static int ehci_hub_control ( @@ -1086,7 +1222,13 @@ static int ehci_hub_control ( * about the EHCI-specific stuff. */ case USB_PORT_FEAT_TEST: - if (!selector || selector > 5) + if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) { + spin_unlock_irqrestore(&ehci->lock, flags); + retval = ehset_single_step_set_feature(hcd, + wIndex); + spin_lock_irqsave(&ehci->lock, flags); + break; + } else if (!selector || selector > 5) goto error; spin_unlock_irqrestore(&ehci->lock, flags); ehci_quiesce(ehci); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index b637a65e1e52..903d00dd6683 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1138,6 +1138,107 @@ submit_async ( return rc; } +/*-------------------------------------------------------------------------*/ +/* + * This function creates the qtds and submits them for the + * SINGLE_STEP_SET_FEATURE Test. + * This is done in two parts: first SETUP req for GetDesc is sent then + * 15 seconds later, the IN stage for GetDesc starts to req data from dev + * + * is_setup : i/p arguement decides which of the two stage needs to be + * performed; TRUE - SETUP and FALSE - IN+STATUS + * Returns 0 if success + */ +static int submit_single_step_set_feature( + struct usb_hcd *hcd, + struct urb *urb, + int is_setup +) { + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct list_head qtd_list; + struct list_head *head; + + struct ehci_qtd *qtd, *qtd_prev; + dma_addr_t buf; + int len, maxpacket; + u32 token; + + INIT_LIST_HEAD(&qtd_list); + head = &qtd_list; + + /* URBs map to sequences of QTDs: one logical transaction */ + qtd = ehci_qtd_alloc(ehci, GFP_KERNEL); + if (unlikely(!qtd)) + return -1; + list_add_tail(&qtd->qtd_list, head); + qtd->urb = urb; + + token = QTD_STS_ACTIVE; + token |= (EHCI_TUNE_CERR << 10); + + len = urb->transfer_buffer_length; + /* + * Check if the request is to perform just the SETUP stage (getDesc) + * as in SINGLE_STEP_SET_FEATURE test, DATA stage (IN) happens + * 15 secs after the setup + */ + if (is_setup) { + /* SETUP pid */ + qtd_fill(ehci, qtd, urb->setup_dma, + sizeof(struct usb_ctrlrequest), + token | (2 /* "setup" */ << 8), 8); + + submit_async(ehci, urb, &qtd_list, GFP_ATOMIC); + return 0; /*Return now; we shall come back after 15 seconds*/ + } + + /* + * IN: data transfer stage: buffer setup : start the IN txn phase for + * the get_Desc SETUP which was sent 15seconds back + */ + token ^= QTD_TOGGLE; /*We need to start IN with DATA-1 Pid-sequence*/ + buf = urb->transfer_dma; + + token |= (1 /* "in" */ << 8); /*This is IN stage*/ + + maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, 0)); + + qtd_fill(ehci, qtd, buf, len, token, maxpacket); + + /* + * Our IN phase shall always be a short read; so keep the queue running + * and let it advance to the next qtd which zero length OUT status + */ + qtd->hw_alt_next = EHCI_LIST_END(ehci); + + /* STATUS stage for GetDesc control request */ + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + + qtd_prev = qtd; + qtd = ehci_qtd_alloc(ehci, GFP_ATOMIC); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + + /* dont fill any data in such packets */ + qtd_fill(ehci, qtd, 0, 0, token, 0); + + /* by default, enable interrupt on urb completion */ + if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) + qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC); + + submit_async(ehci, urb, &qtd_list, GFP_KERNEL); + + return 0; + +cleanup: + qtd_list_free(ehci, urb, head); + return -1; +} + /*-------------------------------------------------------------------------*/ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) diff --git a/drivers/usb/misc/ehset.c b/drivers/usb/misc/ehset.c index d2864b3f41fe..c31b4a33e6bb 100644 --- a/drivers/usb/misc/ehset.c +++ b/drivers/usb/misc/ehset.c @@ -96,7 +96,25 @@ static int ehset_probe(struct usb_interface *intf, kfree(buf); break; case TEST_SINGLE_STEP_SET_FEATURE: - /* unsupported for now */ + /* + * GetDescriptor SETUP request -> 15secs delay -> IN & STATUS + * + * Note, this test is only supported on root hubs since the + * SetPortFeature handling can only be done inside the HCD's + * hub_control callback function. + */ + if (hub_udev != dev->bus->root_hub) { + dev_err(&intf->dev, "SINGLE_STEP_SET_FEATURE test only supported on root hub\n"); + break; + } + + ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, + USB_PORT_FEAT_TEST, + (6 << 8) | portnum, + NULL, 0, 60 * 1000); + + break; default: dev_err(&intf->dev, "%s: unsupported PID: 0x%x\n", __func__, test_pid); -- cgit v1.2.3-58-ga151 From 467d296f47731bcba578cfdea8416b4c152c4f1b Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Thu, 8 Aug 2013 12:11:45 -0500 Subject: wusbcore: fix root hub hub_status_data to only return > 0 if status has actually changed The hub_status_data function on the wireless USB root hub controller (wusbhc_rh_status_data) always returns a positive value even if no ports have changed. This patch updates wusbhc_rh_status_data to only return a positive value if the root hub status needs to be queried. The current implementation can also leave the upper bits of the port bitmap uninitialized if wusbhc->ports_max is not one less than an even multiple of 8. This patch fixes that as well by initializing the buffer to 0. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/rh.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c index bdb0cc3046b5..fe8bc777ab88 100644 --- a/drivers/usb/wusbcore/rh.c +++ b/drivers/usb/wusbcore/rh.c @@ -141,18 +141,26 @@ static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx) int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - size_t cnt, size; - unsigned long *buf = (unsigned long *) _buf; + size_t cnt, size, bits_set = 0; /* WE DON'T LOCK, see comment */ - size = wusbhc->ports_max + 1 /* hub bit */; - size = (size + 8 - 1) / 8; /* round to bytes */ - for (cnt = 0; cnt < wusbhc->ports_max; cnt++) - if (wusb_port_by_idx(wusbhc, cnt)->change) - set_bit(cnt + 1, buf); - else - clear_bit(cnt + 1, buf); - return size; + /* round up to bytes. Hub bit is bit 0 so add 1. */ + size = DIV_ROUND_UP(wusbhc->ports_max + 1, 8); + + /* clear the output buffer. */ + memset(_buf, 0, size); + /* set the bit for each changed port. */ + for (cnt = 0; cnt < wusbhc->ports_max; cnt++) { + + if (wusb_port_by_idx(wusbhc, cnt)->change) { + const int bitpos = cnt+1; + + _buf[bitpos/8] |= (1 << (bitpos % 8)); + bits_set++; + } + } + + return bits_set ? size : 0; } EXPORT_SYMBOL_GPL(wusbhc_rh_status_data); -- cgit v1.2.3-58-ga151 From e9a088fae5e4f9be729f3f419627abc3886b09de Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Mon, 12 Aug 2013 10:10:53 -0500 Subject: wusbcore: clean up list locking in urb enqueue wa_urb_enqueue_run locks and unlocks its list lock as it traverses the list of queued transfers. This was done to prevent deadlocking due to acquiring locks in reverse order in different places. The problem is that releasing the lock during the list traversal could allow the dequeue routine to corrupt the list while it is being iterated over. This patch moves all list entries to a temp list while holding the list lock, then traverses the temp list with no lock held. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 16968c899493..f5c81afc6e96 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -928,7 +928,7 @@ static void wa_xfer_delayed_run(struct wa_rpipe *rpipe) spin_lock_irqsave(&rpipe->seg_lock, flags); while (atomic_read(&rpipe->segs_available) > 0 && !list_empty(&rpipe->seg_list)) { - seg = list_entry(rpipe->seg_list.next, struct wa_seg, + seg = list_first_entry(&(rpipe->seg_list), struct wa_seg, list_node); list_del(&seg->list_node); xfer = seg->xfer; @@ -1093,30 +1093,35 @@ error_xfer_submit: * * We need to be careful here, as dequeue() could be called in the * middle. That's why we do the whole thing under the - * wa->xfer_list_lock. If dequeue() jumps in, it first locks urb->lock + * wa->xfer_list_lock. If dequeue() jumps in, it first locks xfer->lock * and then checks the list -- so as we would be acquiring in inverse - * order, we just drop the lock once we have the xfer and reacquire it - * later. + * order, we move the delayed list to a separate list while locked and then + * submit them without the list lock held. */ void wa_urb_enqueue_run(struct work_struct *ws) { struct wahc *wa = container_of(ws, struct wahc, xfer_work); struct wa_xfer *xfer, *next; struct urb *urb; + LIST_HEAD(tmp_list); + /* Create a copy of the wa->xfer_delayed_list while holding the lock */ spin_lock_irq(&wa->xfer_list_lock); - list_for_each_entry_safe(xfer, next, &wa->xfer_delayed_list, - list_node) { + list_cut_position(&tmp_list, &wa->xfer_delayed_list, + wa->xfer_delayed_list.prev); + spin_unlock_irq(&wa->xfer_list_lock); + + /* + * enqueue from temp list without list lock held since wa_urb_enqueue_b + * can take xfer->lock as well as lock mutexes. + */ + list_for_each_entry_safe(xfer, next, &tmp_list, list_node) { list_del_init(&xfer->list_node); - spin_unlock_irq(&wa->xfer_list_lock); urb = xfer->urb; wa_urb_enqueue_b(xfer); usb_put_urb(urb); /* taken when queuing */ - - spin_lock_irq(&wa->xfer_list_lock); } - spin_unlock_irq(&wa->xfer_list_lock); } EXPORT_SYMBOL_GPL(wa_urb_enqueue_run); -- cgit v1.2.3-58-ga151 From 6dd433e6cf2475ce8abec1b467720858c24450eb Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 6 Aug 2013 14:22:59 +0200 Subject: USB: cdc-wdm: fix race between interrupt handler and tasklet Both could want to submit the same URB. Some checks of the flag intended to prevent that were missing. Signed-off-by: Oliver Neukum CC: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 8a230f0ef77c..d3318a0df8ee 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -209,6 +209,7 @@ skip_error: static void wdm_int_callback(struct urb *urb) { int rv = 0; + int responding; int status = urb->status; struct wdm_device *desc; struct usb_cdc_notification *dr; @@ -262,8 +263,8 @@ static void wdm_int_callback(struct urb *urb) spin_lock(&desc->iuspin); clear_bit(WDM_READ, &desc->flags); - set_bit(WDM_RESPONDING, &desc->flags); - if (!test_bit(WDM_DISCONNECTING, &desc->flags) + responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); + if (!responding && !test_bit(WDM_DISCONNECTING, &desc->flags) && !test_bit(WDM_SUSPENDING, &desc->flags)) { rv = usb_submit_urb(desc->response, GFP_ATOMIC); dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", @@ -685,16 +686,20 @@ static void wdm_rxwork(struct work_struct *work) { struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); unsigned long flags; - int rv; + int rv = 0; + int responding; spin_lock_irqsave(&desc->iuspin, flags); if (test_bit(WDM_DISCONNECTING, &desc->flags)) { spin_unlock_irqrestore(&desc->iuspin, flags); } else { + responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); - rv = usb_submit_urb(desc->response, GFP_KERNEL); + if (!responding) + rv = usb_submit_urb(desc->response, GFP_KERNEL); if (rv < 0 && rv != -EPERM) { spin_lock_irqsave(&desc->iuspin, flags); + clear_bit(WDM_RESPONDING, &desc->flags); if (!test_bit(WDM_DISCONNECTING, &desc->flags)) schedule_work(&desc->rxwork); spin_unlock_irqrestore(&desc->iuspin, flags); -- cgit v1.2.3-58-ga151 From 644f6a121af0aee94b243d33cfe38d6d474e8cbc Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Thu, 8 Aug 2013 15:25:47 -0500 Subject: HWA: avoid constant suspend and resume on the root hub Prevent the USB core from suspending the HWA root hub since bus_suspend and bus_resume are not yet supported. Otherwise the PM system will chew up CPU time constantly attempting to suspend and resume the root hub but never succeeding. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/hwa-hc.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 483990c716aa..5b86ffb88f1c 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -161,6 +161,13 @@ static int hwahc_op_start(struct usb_hcd *usb_hcd) usb_hcd->uses_new_polling = 1; set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags); usb_hcd->state = HC_STATE_RUNNING; + + /* + * prevent USB core from suspending the root hub since + * bus_suspend and bus_resume are not yet supported. + */ + pm_runtime_get_noresume(&usb_hcd->self.root_hub->dev); + result = 0; out: mutex_unlock(&wusbhc->mutex); -- cgit v1.2.3-58-ga151 From 57ce61aad748ceaa08c859da04043ad7dae7c15e Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Tue, 6 Aug 2013 19:26:23 +0200 Subject: usb: pl2303: fix+improve the divsor based baud rate encoding method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the formula in the code description, Reinhard Max and me have investigated the devices behavior / functional principle of the divisor based baud rate encoding method. It turned out, that (although beeing a good starting point) the current code has some flaws. It doesn't work correctly for a wide range of baud rates and the divisor resolution can be improved. It also doesn't report the actually set baud rate. This patch fixes and improves the code for the divisor based baud rate encoding method a lot. It can now be used for the whole range of baud rates from 46 baud to 24M baud with a very good divisor resolution and userspace can read back the resulting baud rate. It also documents the formula used for encoding and the hardware behavior (including special cases). The basic algorithm, rounding and several code comments/explanations are provided by Reinhard Max. I've added some minor fixes, the handling of the special cases and further code/algorithm descriptions. Signed-off-by: Frank Schäfer Signed-off-by: Reinhard Max Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 62 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 244820193e10..b93b3b30dec5 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -4,6 +4,11 @@ * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2003 IBM Corp. * + * Copyright (C) 2009, 2013 Frank Schäfer + * - fixes, improvements and documentation for the baud rate encoding methods + * Copyright (C) 2013 Reinhard Max + * - fixes and improvements for the divisor based baud rate encoding method + * * Original driver for 2.2.x by anonymous * * This program is free software; you can redistribute it and/or @@ -310,21 +315,58 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, put_unaligned_le32(baud, buf); } else { /* + * Divisor based baud rate encoding method + * * NOTE: it's not clear if the type_0/1 chips * support this method * - * Apparently the formula for higher speeds is: - * baudrate = 12M * 32 / (2^buf[1]) / buf[0] + * divisor = 12MHz * 32 / baudrate = 2^A * B + * + * with + * + * A = buf[1] & 0x0e + * B = buf[0] + (buf[1] & 0x01) << 8 + * + * Special cases: + * => 8 < B < 16: device seems to work not properly + * => B <= 8: device uses the max. value B = 512 instead */ - unsigned tmp = 12000000 * 32 / baud; - buf[3] = 0x80; - buf[2] = 0; - buf[1] = (tmp >= 256); - while (tmp >= 256) { - tmp >>= 2; - buf[1] <<= 1; + + /* Determine factors A and B */ + unsigned int A = 0; + unsigned int B = 12000000 * 32 / baud; /* 12MHz */ + B <<= 1; /* Add one bit for rounding */ + while (B > (512 << 1) && A <= 14) { + A += 2; + B >>= 2; } - buf[0] = tmp; + if (A > 14) { /* max. divisor = min. baudrate reached */ + A = 14; + B = 512; + /* => ~45.78 baud */ + } else { + B = (B + 1) >> 1; /* Round the last bit */ + } + /* Handle special cases */ + if (B == 512) + B = 0; /* also: 1 to 8 */ + else if (B < 16) + /* + * NOTE: With the current algorithm this happens + * only for A=0 and means that the min. divisor + * (respectively: the max. baudrate) is reached. + */ + B = 16; /* => 24 MBaud */ + /* Encode the baud rate */ + buf[3] = 0x80; /* Select divisor encoding method */ + buf[2] = 0; + buf[1] = (A & 0x0e); /* A */ + buf[1] |= ((B & 0x100) >> 8); /* MSB of B */ + buf[0] = B & 0xff; /* 8 LSBs of B */ + /* Calculate the actual/resulting baud rate */ + if (B <= 8) + B = 512; + baud = 12000000 * 32 / ((1 << A) * B); } /* Save resulting baud rate */ -- cgit v1.2.3-58-ga151 From 75417d9f99f89ab241de69d7db15af5842b488c4 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Tue, 6 Aug 2013 19:26:24 +0200 Subject: usb: pl2303: do not round to the next nearest standard baud rate for the divisor based baud rate encoding method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In opposition to the direct baud rate encoding method, the divisor based method is not limited to a fixed set of standard baud rates. Hence, there is no need to round to the next nearest standard value. Reported-by: Mastro Gippo Signed-off-by: Frank Schäfer Signed-off-by: Reinhard Max Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 65 ++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 28 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index b93b3b30dec5..04390dff926a 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -273,44 +273,46 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, struct usb_serial_port *port, u8 buf[4]) { - const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, - 4800, 7200, 9600, 14400, 19200, 28800, 38400, - 57600, 115200, 230400, 460800, 500000, 614400, - 921600, 1228800, 2457600, 3000000, 6000000 }; - struct usb_serial *serial = port->serial; struct pl2303_serial_private *spriv = usb_get_serial_data(serial); int baud; - int i; - /* - * NOTE: Only the values defined in baud_sup are supported! - * => if unsupported values are set, the PL2303 seems to use - * 9600 baud (at least my PL2303X always does) - */ baud = tty_get_baud_rate(tty); dev_dbg(&port->dev, "baud requested = %d\n", baud); if (!baud) return; - /* Set baudrate to nearest supported value */ - for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) { - if (baud_sup[i] > baud) - break; - } + if (spriv->type != HX || baud <= 115200) { + /* + * NOTE: Only the values defined in baud_sup are supported ! + * => if unsupported values are set, the PL2303 seems to + * use 9600 baud (at least my PL2303X always does) + */ + const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, + 3600, 4800, 7200, 9600, 14400, 19200, + 28800, 38400, 57600, 115200, 230400, + 460800, 500000, 614400, 921600, + 1228800, 2457600, 3000000, 6000000 }; + int i; + + /* Set baudrate to nearest supported value */ + for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) { + if (baud_sup[i] > baud) + break; + } - if (i == ARRAY_SIZE(baud_sup)) - baud = baud_sup[i - 1]; - else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1])) - baud = baud_sup[i - 1]; - else - baud = baud_sup[i]; + if (i == ARRAY_SIZE(baud_sup)) + baud = baud_sup[i - 1]; + else if (i > 0 + && (baud_sup[i] - baud) > (baud - baud_sup[i - 1])) + baud = baud_sup[i - 1]; + else + baud = baud_sup[i]; - /* type_0, type_1 only support up to 1228800 baud */ - if (spriv->type != HX) - baud = min_t(int, baud, 1228800); + /* type_0, type_1 only support up to 1228800 baud */ + if (spriv->type != HX) + baud = min_t(int, baud, 1228800); - if (spriv->type != HX || baud <= 115200) { /* Direct (standard) baud rate encoding method */ put_unaligned_le32(baud, buf); } else { @@ -331,10 +333,17 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, * => 8 < B < 16: device seems to work not properly * => B <= 8: device uses the max. value B = 512 instead */ + unsigned int A, B; + /* Respect the specified baud rate limits */ + baud = max_t(int, baud, 75); + if (spriv->type == HX) + baud = min_t(int, baud, 6000000); + else + baud = min_t(int, baud, 1228800); /* Determine factors A and B */ - unsigned int A = 0; - unsigned int B = 12000000 * 32 / baud; /* 12MHz */ + A = 0; + B = 12000000 * 32 / baud; /* 12MHz */ B <<= 1; /* Add one bit for rounding */ while (B > (512 << 1) && A <= 14) { A += 2; -- cgit v1.2.3-58-ga151 From b9208c721ce736125fe58d398319513a27850fd8 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Tue, 6 Aug 2013 19:26:25 +0200 Subject: usb: pl2303: remove 500000 baud from the list of standard baud rates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 0c967e7e "USB: serial: pl2303 works at 500kbps" added 500000 baud to the list of supported standard baud rates. But the reason why the driver works with this baud rate is, that since commit 8d48fdf6 "USB: PL2303: correctly handle baudrates above 115200" a second (divisor based) baud rate encoding method is used for values above 115200 baud, which is not limited to a fixed set of standard baud rates. Remove the 500000 baud value from the list of standard baud rates again, because this list is only used with the direct baud rate encoding method and 500000 baud is not supported with this method. Signed-off-by: Frank Schäfer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 04390dff926a..a0ea92ed35c6 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -291,8 +291,8 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200, 230400, - 460800, 500000, 614400, 921600, - 1228800, 2457600, 3000000, 6000000 }; + 460800, 614400, 921600, 1228800, + 2457600, 3000000, 6000000 }; int i; /* Set baudrate to nearest supported value */ -- cgit v1.2.3-58-ga151 From e917ba01d69ad705a4cd6a6c77538f55d84f5907 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Tue, 6 Aug 2013 19:26:26 +0200 Subject: usb: pl2303: move the two baud rate encoding methods to separate functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 215 +++++++++++++++++++++++--------------------- 1 file changed, 114 insertions(+), 101 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index a0ea92ed35c6..693ed7e4871a 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -269,115 +269,128 @@ static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value) return retval; } +static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type, + u8 buf[4]) +{ + /* + * NOTE: Only the values defined in baud_sup are supported ! + * => if unsupported values are set, the PL2303 seems to + * use 9600 baud (at least my PL2303X always does) + */ + const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, + 4800, 7200, 9600, 14400, 19200, 28800, 38400, + 57600, 115200, 230400, 460800, 614400, 921600, + 1228800, 2457600, 3000000, 6000000 }; + int i; + + /* Set baudrate to nearest supported value */ + for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) { + if (baud_sup[i] > baud) + break; + } + if (i == ARRAY_SIZE(baud_sup)) + baud = baud_sup[i - 1]; + else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1])) + baud = baud_sup[i - 1]; + else + baud = baud_sup[i]; + /* type_0, type_1 only support up to 1228800 baud */ + if (type != HX) + baud = min_t(int, baud, 1228800); + /* Direct (standard) baud rate encoding method */ + put_unaligned_le32(baud, buf); + + return baud; +} + +static int pl2303_baudrate_encode_divisor(int baud, enum pl2303_type type, + u8 buf[4]) +{ + /* + * Divisor based baud rate encoding method + * + * NOTE: it's not clear if the type_0/1 chips support this method + * + * divisor = 12MHz * 32 / baudrate = 2^A * B + * + * with + * + * A = buf[1] & 0x0e + * B = buf[0] + (buf[1] & 0x01) << 8 + * + * Special cases: + * => 8 < B < 16: device seems to work not properly + * => B <= 8: device uses the max. value B = 512 instead + */ + unsigned int A, B; + + /* Respect the specified baud rate limits */ + baud = max_t(int, baud, 75); + if (type == HX) + baud = min_t(int, baud, 6000000); + else + baud = min_t(int, baud, 1228800); + /* Determine factors A and B */ + A = 0; + B = 12000000 * 32 / baud; /* 12MHz */ + B <<= 1; /* Add one bit for rounding */ + while (B > (512 << 1) && A <= 14) { + A += 2; + B >>= 2; + } + if (A > 14) { /* max. divisor = min. baudrate reached */ + A = 14; + B = 512; + /* => ~45.78 baud */ + } else { + B = (B + 1) >> 1; /* Round the last bit */ + } + /* Handle special cases */ + if (B == 512) + B = 0; /* also: 1 to 8 */ + else if (B < 16) + /* + * NOTE: With the current algorithm this happens + * only for A=0 and means that the min. divisor + * (respectively: the max. baudrate) is reached. + */ + B = 16; /* => 24 MBaud */ + /* Encode the baud rate */ + buf[3] = 0x80; /* Select divisor encoding method */ + buf[2] = 0; + buf[1] = (A & 0x0e); /* A */ + buf[1] |= ((B & 0x100) >> 8); /* MSB of B */ + buf[0] = B & 0xff; /* 8 LSBs of B */ + /* Calculate the actual/resulting baud rate */ + if (B <= 8) + B = 512; + baud = 12000000 * 32 / ((1 << A) * B); + + return baud; +} + static void pl2303_encode_baudrate(struct tty_struct *tty, struct usb_serial_port *port, + enum pl2303_type type, u8 buf[4]) { - struct usb_serial *serial = port->serial; - struct pl2303_serial_private *spriv = usb_get_serial_data(serial); int baud; baud = tty_get_baud_rate(tty); dev_dbg(&port->dev, "baud requested = %d\n", baud); if (!baud) return; - - if (spriv->type != HX || baud <= 115200) { - /* - * NOTE: Only the values defined in baud_sup are supported ! - * => if unsupported values are set, the PL2303 seems to - * use 9600 baud (at least my PL2303X always does) - */ - const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, - 3600, 4800, 7200, 9600, 14400, 19200, - 28800, 38400, 57600, 115200, 230400, - 460800, 614400, 921600, 1228800, - 2457600, 3000000, 6000000 }; - int i; - - /* Set baudrate to nearest supported value */ - for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) { - if (baud_sup[i] > baud) - break; - } - - if (i == ARRAY_SIZE(baud_sup)) - baud = baud_sup[i - 1]; - else if (i > 0 - && (baud_sup[i] - baud) > (baud - baud_sup[i - 1])) - baud = baud_sup[i - 1]; - else - baud = baud_sup[i]; - - /* type_0, type_1 only support up to 1228800 baud */ - if (spriv->type != HX) - baud = min_t(int, baud, 1228800); - - /* Direct (standard) baud rate encoding method */ - put_unaligned_le32(baud, buf); - } else { - /* - * Divisor based baud rate encoding method - * - * NOTE: it's not clear if the type_0/1 chips - * support this method - * - * divisor = 12MHz * 32 / baudrate = 2^A * B - * - * with - * - * A = buf[1] & 0x0e - * B = buf[0] + (buf[1] & 0x01) << 8 - * - * Special cases: - * => 8 < B < 16: device seems to work not properly - * => B <= 8: device uses the max. value B = 512 instead - */ - unsigned int A, B; - - /* Respect the specified baud rate limits */ - baud = max_t(int, baud, 75); - if (spriv->type == HX) - baud = min_t(int, baud, 6000000); - else - baud = min_t(int, baud, 1228800); - /* Determine factors A and B */ - A = 0; - B = 12000000 * 32 / baud; /* 12MHz */ - B <<= 1; /* Add one bit for rounding */ - while (B > (512 << 1) && A <= 14) { - A += 2; - B >>= 2; - } - if (A > 14) { /* max. divisor = min. baudrate reached */ - A = 14; - B = 512; - /* => ~45.78 baud */ - } else { - B = (B + 1) >> 1; /* Round the last bit */ - } - /* Handle special cases */ - if (B == 512) - B = 0; /* also: 1 to 8 */ - else if (B < 16) - /* - * NOTE: With the current algorithm this happens - * only for A=0 and means that the min. divisor - * (respectively: the max. baudrate) is reached. - */ - B = 16; /* => 24 MBaud */ - /* Encode the baud rate */ - buf[3] = 0x80; /* Select divisor encoding method */ - buf[2] = 0; - buf[1] = (A & 0x0e); /* A */ - buf[1] |= ((B & 0x100) >> 8); /* MSB of B */ - buf[0] = B & 0xff; /* 8 LSBs of B */ - /* Calculate the actual/resulting baud rate */ - if (B <= 8) - B = 512; - baud = 12000000 * 32 / ((1 << A) * B); - } - + /* + * There are two methods for setting/encoding the baud rate + * 1) Direct method: encodes the baud rate value directly + * => supported by all chip types + * 2) Divisor based method: encodes a divisor to a base value (12MHz*32) + * => supported by HX chips (and likely not by type_0/1 chips) + */ + if (type != HX || baud <= 115200) + baud = pl2303_baudrate_encode_direct(baud, type, buf); + else + baud = pl2303_baudrate_encode_divisor(baud, type, buf); /* Save resulting baud rate */ tty_encode_baud_rate(tty, baud, baud); dev_dbg(&port->dev, "baud set = %d\n", baud); @@ -434,8 +447,8 @@ static void pl2303_set_termios(struct tty_struct *tty, dev_dbg(&port->dev, "data bits = %d\n", buf[6]); } - /* For reference buf[0]:buf[3] baud rate value */ - pl2303_encode_baudrate(tty, port, &buf[0]); + /* For reference: buf[0]:buf[3] baud rate value */ + pl2303_encode_baudrate(tty, port, spriv->type, buf); /* For reference buf[4]=0 is 1 stop bits */ /* For reference buf[4]=1 is 1.5 stop bits */ -- cgit v1.2.3-58-ga151 From b5c16c6a031c52cc4b7dda6c3de46462fbc92eab Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Tue, 6 Aug 2013 19:26:27 +0200 Subject: usb: pl2303: increase the allowed baud rate range for the divisor based encoding method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reinhard Max has done some tests with a PL2303HX (rev A) and a logic analyzer and it seems, that although the PL2303HX is specified for baud rates from 75 to 6M baud, the full divisor range can be used with the divisor based baud rate encoding method. This corresponds to baud rates from 46 to 24M baud. Baud rates down to 46 baud (max. divisor) have been confirmed to work even under heavy/permanent load, so remove the lower limit. Baud rates up to 24M baud should really be tested carefully in "real life" scenarios before removing the upper limit completely. Anyway, the Windows driver allows maximum baud rates of 110% of the specified limit, so for now, increase the upper limit to this value. Signed-off-by: Frank Schäfer Signed-off-by: Reinhard Max Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 693ed7e4871a..61c9f9d28ee9 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -324,12 +324,20 @@ static int pl2303_baudrate_encode_divisor(int baud, enum pl2303_type type, */ unsigned int A, B; - /* Respect the specified baud rate limits */ - baud = max_t(int, baud, 75); + /* + * NOTE: The Windows driver allows maximum baud rates of 110% of the + * specified maximium value. + * Quick tests with early (2004) HX (rev. A) chips suggest, that even + * higher baud rates (up to the maximum of 24M baud !) are working fine, + * but that should really be tested carefully in "real life" scenarios + * before removing the upper limit completely. + * Baud rates smaller than the specified 75 baud are definitely working + * fine. + */ if (type == HX) - baud = min_t(int, baud, 6000000); + baud = min_t(int, baud, 6000000 * 1.1); else - baud = min_t(int, baud, 1228800); + baud = min_t(int, baud, 1228800 * 1.1); /* Determine factors A and B */ A = 0; B = 12000000 * 32 / baud; /* 12MHz */ -- cgit v1.2.3-58-ga151 From 61fa8d694b8547894b57ea0d99d0120a58f6ebf8 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Tue, 6 Aug 2013 19:26:28 +0200 Subject: usb: pl2303: also use the divisor based baud rate encoding method for baud rates < 115200 with HX chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the divisor based baud rate encoding method has been fixed and extended, it can also be used for baud rates < 115200 baud with HX chips. This makes it possible to adjust the baud rate almost continuously instead of just beeing able to select between 16 fixed standard values. Tested with a PL2303HX 04463A (week 46, 2004, rev 3A). Signed-off-by: Frank Schäfer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 61c9f9d28ee9..09fb55cf3be7 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -395,7 +395,7 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, * 2) Divisor based method: encodes a divisor to a base value (12MHz*32) * => supported by HX chips (and likely not by type_0/1 chips) */ - if (type != HX || baud <= 115200) + if (type != HX) baud = pl2303_baudrate_encode_direct(baud, type, buf); else baud = pl2303_baudrate_encode_divisor(baud, type, buf); -- cgit v1.2.3-58-ga151 From c23bda365dfbf56aa4d6d4a97f83136c36050e01 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Tue, 6 Aug 2013 19:26:29 +0200 Subject: usb: pl2303: add two comments concerning the supported baud rates with HX chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've found some new datasheets which describe some additionally supported standard baud rates and I've verified them with my HX (rev. 3A) device. But adding support for individual (chip type specific) baud rates would add a good amount of extra code (especially when support for further chips will be added to the driver one day), which makes no sense as long as we are not using the direct baud rate encoding method for newer chips. So for now, just drop a comment about these additionally supported baud rates. The second comment is about the baud rate differences between the two encoding methods. In theory, we could optimize the code a bit by comparing the resulting baud rates of both methods and selecting the one which is closer to the requested baud rate. But that seems to be a bit overkill, because the differences are very small and the device likely uses the same baud rate generator for both methods so that the resulting baud rate would be the same. Signed-off-by: Frank Schäfer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 09fb55cf3be7..6638c5d9079d 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -281,6 +281,12 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type, 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800, 614400, 921600, 1228800, 2457600, 3000000, 6000000 }; + /* + * NOTE: The PL2303HX (tested with rev. 3A) also supports the following + * baud rates: 128000, 134400, 161280, 201600, 268800, 403200, 806400. + * As long as we are not using this encoding method for them, there is + * no point in complicating the code to support them. + */ int i; /* Set baudrate to nearest supported value */ @@ -394,6 +400,12 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, * => supported by all chip types * 2) Divisor based method: encodes a divisor to a base value (12MHz*32) * => supported by HX chips (and likely not by type_0/1 chips) + * + * NOTE: Although the divisor based baud rate encoding method is much + * more flexible, some of the standard baud rate values can not be + * realized exactly. But the difference is very small (max. 0.2%) and + * the device likely uses the same baud rate generator for both methods + * so that there is likley no difference. */ if (type != HX) baud = pl2303_baudrate_encode_direct(baud, type, buf); -- cgit v1.2.3-58-ga151 From fc525751457e93783b1c85a0612735a598c8497f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 13 Aug 2013 19:38:23 +0200 Subject: usb: musb: Use is_cppi_enabled() and tusb_dma_omap() instead of the ifdef This patch makes use of the two function is_cppi_enabled() and tusb_dma_omap() instead of the ifdef for the proper DMA implementation setup code. It basically shifts the code right by one indention level and adds a few line breaks once the chars are crossed. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 82 +++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 40 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 96632f9e3eeb..4376f51f5ef1 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -357,47 +357,49 @@ static void txstate(struct musb *musb, struct musb_request *req) } } -#elif defined(CONFIG_USB_TI_CPPI_DMA) - /* program endpoint CSR first, then setup DMA */ - csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); - csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE | - MUSB_TXCSR_MODE; - musb_writew(epio, MUSB_TXCSR, - (MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN) - | csr); - - /* ensure writebuffer is empty */ - csr = musb_readw(epio, MUSB_TXCSR); - - /* NOTE host side sets DMAENAB later than this; both are - * OK since the transfer dma glue (between CPPI and Mentor - * fifos) just tells CPPI it could start. Data only moves - * to the USB TX fifo when both fifos are ready. - */ - - /* "mode" is irrelevant here; handle terminating ZLPs like - * PIO does, since the hardware RNDIS mode seems unreliable - * except for the last-packet-is-already-short case. - */ - use_dma = use_dma && c->channel_program( - musb_ep->dma, musb_ep->packet_sz, - 0, - request->dma + request->actual, - request_size); - if (!use_dma) { - c->channel_release(musb_ep->dma); - musb_ep->dma = NULL; - csr &= ~MUSB_TXCSR_DMAENAB; - musb_writew(epio, MUSB_TXCSR, csr); - /* invariant: prequest->buf is non-null */ - } -#elif defined(CONFIG_USB_TUSB_OMAP_DMA) - use_dma = use_dma && c->channel_program( - musb_ep->dma, musb_ep->packet_sz, - request->zero, - request->dma + request->actual, - request_size); #endif + if (is_cppi_enabled()) { + /* program endpoint CSR first, then setup DMA */ + csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); + csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE | + MUSB_TXCSR_MODE; + musb_writew(epio, MUSB_TXCSR, (MUSB_TXCSR_P_WZC_BITS & + ~MUSB_TXCSR_P_UNDERRUN) | csr); + + /* ensure writebuffer is empty */ + csr = musb_readw(epio, MUSB_TXCSR); + + /* + * NOTE host side sets DMAENAB later than this; both are + * OK since the transfer dma glue (between CPPI and + * Mentor fifos) just tells CPPI it could start. Data + * only moves to the USB TX fifo when both fifos are + * ready. + */ + /* + * "mode" is irrelevant here; handle terminating ZLPs + * like PIO does, since the hardware RNDIS mode seems + * unreliable except for the + * last-packet-is-already-short case. + */ + use_dma = use_dma && c->channel_program( + musb_ep->dma, musb_ep->packet_sz, + 0, + request->dma + request->actual, + request_size); + if (!use_dma) { + c->channel_release(musb_ep->dma); + musb_ep->dma = NULL; + csr &= ~MUSB_TXCSR_DMAENAB; + musb_writew(epio, MUSB_TXCSR, csr); + /* invariant: prequest->buf is non-null */ + } + } else if (tusb_dma_omap()) + use_dma = use_dma && c->channel_program( + musb_ep->dma, musb_ep->packet_sz, + request->zero, + request->dma + request->actual, + request_size); } #endif -- cgit v1.2.3-58-ga151 From 13266fea59f6f55e98d61e66707d784b9e947c84 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 13 Aug 2013 19:38:24 +0200 Subject: usb: musb: cppi41: Enable in device-TX mode Since the musb-gadget code now calls the dma engine properly it is possible to enable it for the TX path in device mode. AM335x Advisory 1.0.13 says that we may lose the toggle bit on multiple RX transfers. There is a workaround in host mode but none in device mode and therefore RX transfers are disabled. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index a74259ea0306..e64701d15401 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -362,6 +362,9 @@ static int cppi41_is_compatible(struct dma_channel *channel, u16 maxpacket, WARN_ON(1); return 1; } + if (cppi41_channel->is_tx) + return 1; + /* AM335x Advisory 1.0.13. No workaround for device RX mode */ return 0; } -- cgit v1.2.3-58-ga151 From 8b841cb217fac676498de3dfe8fabe38b39cba4e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 13 Aug 2013 10:57:08 +0200 Subject: usb: phy: am335x: include linux/err.h Stephen Rothwell reported that this driver does not compile on PowerPC due to this missing include. One could argue why this driver is enabled on PowerPC in the first place but it sure isn't wrong to include headers for used function instead of to rely that they sneak in. Reported-by: Stephen Rothwell Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-am335x-control.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 35494f1c33b6..759754521426 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -1,5 +1,6 @@ #include #include +#include #include #include -- cgit v1.2.3-58-ga151 From 1fe6c4524f765734d376c39d0f41e25519f6235d Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Thu, 25 Jul 2013 19:04:44 -0400 Subject: usb: Add Device Tree support to XHCI Platform driver Add Device Tree match table to xhci-plat.c. Add DT bindings document. Signed-off-by: Al Cooper Cc: Sergei Shtylyov Cc: Felipe Balbi Signed-off-by: Sarah Sharp --- Documentation/devicetree/bindings/usb/usb-xhci.txt | 14 ++++++++++++++ drivers/usb/host/xhci-plat.c | 10 ++++++++++ 2 files changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/usb-xhci.txt (limited to 'drivers/usb') diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt new file mode 100644 index 000000000000..5752df0e17a2 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt @@ -0,0 +1,14 @@ +USB xHCI controllers + +Required properties: + - compatible: should be "xhci-platform". + - reg: should contain address and length of the standard XHCI + register set for the device. + - interrupts: one XHCI interrupt should be described here. + +Example: + usb@f0931000 { + compatible = "xhci-platform"; + reg = <0xf0931000 0x8c8>; + interrupts = <0x0 0x4e 0x0>; + }; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 412fe8d167cf..3fcdaea21e24 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "xhci.h" @@ -211,12 +212,21 @@ static const struct dev_pm_ops xhci_plat_pm_ops = { #define DEV_PM_OPS NULL #endif /* CONFIG_PM */ +#ifdef CONFIG_OF +static const struct of_device_id usb_xhci_of_match[] = { + { .compatible = "xhci-platform" }, + { }, +}; +MODULE_DEVICE_TABLE(of, usb_xhci_of_match); +#endif + static struct platform_driver usb_xhci_driver = { .probe = xhci_plat_probe, .remove = xhci_plat_remove, .driver = { .name = "xhci-hcd", .pm = DEV_PM_OPS, + .of_match_table = of_match_ptr(usb_xhci_of_match), }, }; MODULE_ALIAS("platform:xhci-hcd"); -- cgit v1.2.3-58-ga151 From 38a532a621ad8533d8839ef0ff8bd15dc08b8eab Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Tue, 2 Jul 2013 17:49:25 +0300 Subject: xhci: replace xhci_info() with xhci_dbg() This patch replaces the calls to xhci_info() with calls to xhci_dbg() and removes the unused xhci_info() definition from xhci-hcd. By replacing the xhci_info() with xhci_dbg(), the calls to dev_info() are replaced with calls to dev_dbg() so that their output can be dynamically controlled via the dynamic debugging mechanism. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 4 ++-- drivers/usb/host/xhci.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index c8d7199eefa0..c3b8f1481aaf 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3508,10 +3508,10 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) switch (ret) { case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */ case COMP_CTX_STATE: /* 0.96 completion code for same thing */ - xhci_info(xhci, "Can't reset device (slot ID %u) in %s state\n", + xhci_dbg(xhci, "Can't reset device (slot ID %u) in %s state\n", slot_id, xhci_get_slot_state(xhci, virt_dev->out_ctx)); - xhci_info(xhci, "Not freeing device rings.\n"); + xhci_dbg(xhci, "Not freeing device rings.\n"); /* Don't treat this as an error. May change my mind later. */ ret = 0; goto command_cleanup; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index c338741a675d..0a74d10be8a1 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1587,8 +1587,6 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci) #define xhci_dbg(xhci, fmt, args...) \ do { if (XHCI_DEBUG) dev_dbg(xhci_to_hcd(xhci)->self.controller , fmt , ## args); } while (0) -#define xhci_info(xhci, fmt, args...) \ - do { if (XHCI_DEBUG) dev_info(xhci_to_hcd(xhci)->self.controller , fmt , ## args); } while (0) #define xhci_err(xhci, fmt, args...) \ dev_err(xhci_to_hcd(xhci)->self.controller , fmt , ## args) #define xhci_warn(xhci, fmt, args...) \ -- cgit v1.2.3-58-ga151 From 5c1127d320e02fccebf10ec3d8bce13317bb4d3c Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Tue, 2 Jul 2013 17:49:26 +0300 Subject: xhci: replace printk(KERN_DEBUG ...) This patch replaces the calls to printk(KERN_DEBUG ...) with either calls to xhci_dbg() or calls to pr_debug(), depending on whether the xhci_hcd structure is available at callsite, so that the correspoding debugging messages are not enabled by default when CONFIG_DYNAMIC_DEBUG option is set but rather can be enabled dynamically taking advantage of the dynamic debugging feature. Also, it adds a newline at the end of debugging messages in case there is not, so that messages don't appear broken when printed. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index c3b8f1481aaf..0208ffff99dc 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1170,27 +1170,25 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_virt_device *virt_dev; if (!hcd || (check_ep && !ep) || !udev) { - printk(KERN_DEBUG "xHCI %s called with invalid args\n", - func); + pr_debug("xHCI %s called with invalid args\n", func); return -EINVAL; } if (!udev->parent) { - printk(KERN_DEBUG "xHCI %s called for root hub\n", - func); + pr_debug("xHCI %s called for root hub\n", func); return 0; } xhci = hcd_to_xhci(hcd); if (check_virt_dev) { if (!udev->slot_id || !xhci->devs[udev->slot_id]) { - printk(KERN_DEBUG "xHCI %s called with unaddressed " - "device\n", func); + xhci_dbg(xhci, "xHCI %s called with unaddressed device\n", + func); return -EINVAL; } virt_dev = xhci->devs[udev->slot_id]; if (virt_dev->udev != udev) { - printk(KERN_DEBUG "xHCI %s called with udev and " + xhci_dbg(xhci, "xHCI %s called with udev and " "virt_dev does not match\n", func); return -EINVAL; } @@ -4941,12 +4939,12 @@ static int __init xhci_hcd_init(void) retval = xhci_register_pci(); if (retval < 0) { - printk(KERN_DEBUG "Problem registering PCI driver."); + pr_debug("Problem registering PCI driver.\n"); return retval; } retval = xhci_register_plat(); if (retval < 0) { - printk(KERN_DEBUG "Problem registering platform driver."); + pr_debug("Problem registering platform driver.\n"); goto unreg_pci; } /* -- cgit v1.2.3-58-ga151 From b2497509df002e9a09c8550cd0ecd2f77c9640d8 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Tue, 2 Jul 2013 17:49:27 +0300 Subject: xhci: remove CONFIG_USB_XHCI_HCD_DEBUGGING and unused code CONFIG_USB_XHCI_HCD_DEBUGGING option is used to enable verbose debugging output for the xHCI host controller driver. In the current version of the xhci-hcd driver, this option must be turned on, in order for the debugging log messages to be displayed, and users may need to recompile the linux kernel to obtain debugging information that will help them track down problems. This patch removes the above debug option to enable debugging log messages at all times. The aim of this is to rely on the debugfs and the dynamic debugging feature for fine-grained management of debugging messages and to not force users to set the debug config option and compile the linux kernel in order to have access in that information. This patch, also, removes the XHCI_DEBUG symbol and the functions dma_to_stream_ring(), xhci_test_radix_tree() and xhci_event_ring_work() that are not useful anymore. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/Kconfig | 9 ------ drivers/usb/host/xhci-mem.c | 70 ------------------------------------------ drivers/usb/host/xhci.c | 74 --------------------------------------------- drivers/usb/host/xhci.h | 13 +------- 4 files changed, 1 insertion(+), 165 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index cf521d6551dd..28bc5befeae3 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -29,15 +29,6 @@ if USB_XHCI_HCD config USB_XHCI_PLATFORM tristate -config USB_XHCI_HCD_DEBUGGING - bool "Debugging for the xHCI host controller" - ---help--- - Say 'Y' to turn on debugging for the xHCI host controller driver. - This will spew debugging output, even in interrupt context. - This should only be used for debugging xHCI driver bugs. - - If unsure, say N. - endif # USB_XHCI_HCD config USB_EHCI_HCD diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index df6978abd7e6..b881bc1697f9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -481,17 +481,6 @@ struct xhci_ring *xhci_dma_to_transfer_ring( return ep->ring; } -/* Only use this when you know stream_info is valid */ -#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING -static struct xhci_ring *dma_to_stream_ring( - struct xhci_stream_info *stream_info, - u64 address) -{ - return radix_tree_lookup(&stream_info->trb_address_map, - address >> TRB_SEGMENT_SHIFT); -} -#endif /* CONFIG_USB_XHCI_HCD_DEBUGGING */ - struct xhci_ring *xhci_stream_id_to_ring( struct xhci_virt_device *dev, unsigned int ep_index, @@ -509,58 +498,6 @@ struct xhci_ring *xhci_stream_id_to_ring( return ep->stream_info->stream_rings[stream_id]; } -#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING -static int xhci_test_radix_tree(struct xhci_hcd *xhci, - unsigned int num_streams, - struct xhci_stream_info *stream_info) -{ - u32 cur_stream; - struct xhci_ring *cur_ring; - u64 addr; - - for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { - struct xhci_ring *mapped_ring; - int trb_size = sizeof(union xhci_trb); - - cur_ring = stream_info->stream_rings[cur_stream]; - for (addr = cur_ring->first_seg->dma; - addr < cur_ring->first_seg->dma + TRB_SEGMENT_SIZE; - addr += trb_size) { - mapped_ring = dma_to_stream_ring(stream_info, addr); - if (cur_ring != mapped_ring) { - xhci_warn(xhci, "WARN: DMA address 0x%08llx " - "didn't map to stream ID %u; " - "mapped to ring %p\n", - (unsigned long long) addr, - cur_stream, - mapped_ring); - return -EINVAL; - } - } - /* One TRB after the end of the ring segment shouldn't return a - * pointer to the current ring (although it may be a part of a - * different ring). - */ - mapped_ring = dma_to_stream_ring(stream_info, addr); - if (mapped_ring != cur_ring) { - /* One TRB before should also fail */ - addr = cur_ring->first_seg->dma - trb_size; - mapped_ring = dma_to_stream_ring(stream_info, addr); - } - if (mapped_ring == cur_ring) { - xhci_warn(xhci, "WARN: Bad DMA address 0x%08llx " - "mapped to valid stream ID %u; " - "mapped ring = %p\n", - (unsigned long long) addr, - cur_stream, - mapped_ring); - return -EINVAL; - } - } - return 0; -} -#endif /* CONFIG_USB_XHCI_HCD_DEBUGGING */ - /* * Change an endpoint's internal structure so it supports stream IDs. The * number of requested streams includes stream 0, which cannot be used by device @@ -687,13 +624,6 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, * was any other way, the host controller would assume the ring is * "empty" and wait forever for data to be queued to that stream ID). */ -#if XHCI_DEBUG - /* Do a little test on the radix tree to make sure it returns the - * correct values. - */ - if (xhci_test_radix_tree(xhci, num_streams, stream_info)) - goto cleanup_rings; -#endif return stream_info; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 0208ffff99dc..b87404535c29 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -528,57 +528,6 @@ int xhci_init(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING -static void xhci_event_ring_work(unsigned long arg) -{ - unsigned long flags; - int temp; - u64 temp_64; - struct xhci_hcd *xhci = (struct xhci_hcd *) arg; - int i, j; - - xhci_dbg(xhci, "Poll event ring: %lu\n", jiffies); - - spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, &xhci->op_regs->status); - xhci_dbg(xhci, "op reg status = 0x%x\n", temp); - if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) || - (xhci->xhc_state & XHCI_STATE_HALTED)) { - xhci_dbg(xhci, "HW died, polling stopped.\n"); - spin_unlock_irqrestore(&xhci->lock, flags); - return; - } - - temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); - xhci_dbg(xhci, "ir_set 0 pending = 0x%x\n", temp); - xhci_dbg(xhci, "HC error bitmask = 0x%x\n", xhci->error_bitmask); - xhci->error_bitmask = 0; - xhci_dbg(xhci, "Event ring:\n"); - xhci_debug_segment(xhci, xhci->event_ring->deq_seg); - xhci_dbg_ring_ptrs(xhci, xhci->event_ring); - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - temp_64 &= ~ERST_PTR_MASK; - xhci_dbg(xhci, "ERST deq = 64'h%0lx\n", (long unsigned int) temp_64); - xhci_dbg(xhci, "Command ring:\n"); - xhci_debug_segment(xhci, xhci->cmd_ring->deq_seg); - xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring); - xhci_dbg_cmd_ptrs(xhci); - for (i = 0; i < MAX_HC_SLOTS; ++i) { - if (!xhci->devs[i]) - continue; - for (j = 0; j < 31; ++j) { - xhci_dbg_ep_rings(xhci, i, j, &xhci->devs[i]->eps[j]); - } - } - spin_unlock_irqrestore(&xhci->lock, flags); - - if (!xhci->zombie) - mod_timer(&xhci->event_ring_timer, jiffies + POLL_TIMEOUT * HZ); - else - xhci_dbg(xhci, "Quit polling the event ring.\n"); -} -#endif - static int xhci_run_finished(struct xhci_hcd *xhci) { if (xhci_start(xhci)) { @@ -628,17 +577,6 @@ int xhci_run(struct usb_hcd *hcd) if (ret) return ret; -#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING - init_timer(&xhci->event_ring_timer); - xhci->event_ring_timer.data = (unsigned long) xhci; - xhci->event_ring_timer.function = xhci_event_ring_work; - /* Poll the event ring */ - xhci->event_ring_timer.expires = jiffies + POLL_TIMEOUT * HZ; - xhci->zombie = 0; - xhci_dbg(xhci, "Setting event ring polling timer\n"); - add_timer(&xhci->event_ring_timer); -#endif - xhci_dbg(xhci, "Command ring memory map follows:\n"); xhci_debug_ring(xhci, xhci->cmd_ring); xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring); @@ -725,12 +663,6 @@ void xhci_stop(struct usb_hcd *hcd) xhci_cleanup_msix(xhci); -#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING - /* Tell the event ring poll function not to reschedule */ - xhci->zombie = 1; - del_timer_sync(&xhci->event_ring_timer); -#endif - /* Deleting Compliance Mode Recovery Timer */ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && (!(xhci_all_ports_seen_u0(xhci)))) { @@ -1011,12 +943,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) spin_unlock_irq(&xhci->lock); xhci_cleanup_msix(xhci); -#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING - /* Tell the event ring poll function not to reschedule */ - xhci->zombie = 1; - del_timer_sync(&xhci->event_ring_timer); -#endif - xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = xhci_readl(xhci, &xhci->op_regs->status); xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 0a74d10be8a1..4d43531f5fcf 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1490,11 +1490,6 @@ struct xhci_hcd { struct dma_pool *small_streams_pool; struct dma_pool *medium_streams_pool; -#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING - /* Poll the rings - for debugging */ - struct timer_list event_ring_timer; - int zombie; -#endif /* Host controller watchdog timer structures */ unsigned int xhc_state; @@ -1579,14 +1574,8 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci) return xhci->main_hcd; } -#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING -#define XHCI_DEBUG 1 -#else -#define XHCI_DEBUG 0 -#endif - #define xhci_dbg(xhci, fmt, args...) \ - do { if (XHCI_DEBUG) dev_dbg(xhci_to_hcd(xhci)->self.controller , fmt , ## args); } while (0) + dev_dbg(xhci_to_hcd(xhci)->self.controller , fmt , ## args) #define xhci_err(xhci, fmt, args...) \ dev_err(xhci_to_hcd(xhci)->self.controller , fmt , ## args) #define xhci_warn(xhci, fmt, args...) \ -- cgit v1.2.3-58-ga151 From 84a99f6fc5d4a2b7583aa287979268a8bb926367 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Tue, 6 Aug 2013 00:22:15 +0300 Subject: xhci: add traces for debug messages in xhci_address_device() This patch declares an event class for trace events that trace messages with variadic arguments, called xhci_log_msg, and defines a trace event for tracing the debug messages in xhci_address_device() function, called xhci_dbg_address. In order to implement this type of trace events, a wrapper function, called xhci_dbg_trace(), was created that records the format string and variadic arguments into a va_format structure which is passed as argument to the tracepoints of the class xhci_log_msg. All the xhci_dbg() calls in xhci_address_device() are replaced with calls to xhci_dbg_trace(). The functionality of xhci_dbg() log messages was not removed though, but it is placed inside xhci_dbg_trace(). This trace event aims to give the ability to the user or the developper to isolate and trace the debug messages generated when an Address Device Command is issued to xHC. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/Makefile | 4 ++++ drivers/usb/host/xhci-dbg.c | 14 +++++++++++++ drivers/usb/host/xhci-trace.c | 15 +++++++++++++ drivers/usb/host/xhci-trace.h | 49 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.c | 30 ++++++++++++++++---------- drivers/usb/host/xhci.h | 2 ++ 6 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 drivers/usb/host/xhci-trace.c create mode 100644 drivers/usb/host/xhci-trace.h (limited to 'drivers/usb') diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 829a3397882a..50b0041c09a9 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -4,6 +4,9 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG +# tell define_trace.h where to find the xhci trace header +CFLAGS_xhci-trace.o := -I$(src) + isp1760-y := isp1760-hcd.o isp1760-if.o fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o @@ -13,6 +16,7 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o xhci-hcd-y := xhci.o xhci-mem.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o +xhci-hcd-y += xhci-trace.o xhci-hcd-$(CONFIG_PCI) += xhci-pci.o ifneq ($(CONFIG_USB_XHCI_PLATFORM), ) diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 5d5e58fdeccc..73503a81ee81 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -580,3 +580,17 @@ void xhci_dbg_ctx(struct xhci_hcd *xhci, xhci_dbg_slot_ctx(xhci, ctx); xhci_dbg_ep_ctx(xhci, ctx, last_ep); } + +void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + xhci_dbg(xhci, "%pV\n", &vaf); + trace(&vaf); + va_end(args); +} diff --git a/drivers/usb/host/xhci-trace.c b/drivers/usb/host/xhci-trace.c new file mode 100644 index 000000000000..7cf30c83dcf3 --- /dev/null +++ b/drivers/usb/host/xhci-trace.c @@ -0,0 +1,15 @@ +/* + * xHCI host controller driver + * + * Copyright (C) 2013 Xenia Ragiadakou + * + * Author: Xenia Ragiadakou + * Email : burzalodowa@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define CREATE_TRACE_POINTS +#include "xhci-trace.h" diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h new file mode 100644 index 000000000000..432aa675966c --- /dev/null +++ b/drivers/usb/host/xhci-trace.h @@ -0,0 +1,49 @@ +/* + * xHCI host controller driver + * + * Copyright (C) 2013 Xenia Ragiadakou + * + * Author: Xenia Ragiadakou + * Email : burzalodowa@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM xhci-hcd + +#if !defined(__XHCI_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __XHCI_TRACE_H + +#include + +#define XHCI_MSG_MAX 500 + +DECLARE_EVENT_CLASS(xhci_log_msg, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry(__dynamic_array(char, msg, XHCI_MSG_MAX)), + TP_fast_assign( + vsnprintf(__get_str(msg), XHCI_MSG_MAX, vaf->fmt, *vaf->va); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(xhci_log_msg, xhci_dbg_address, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +#endif /* __XHCI_TRACE_H */ + +/* this part must be outside header guard */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE xhci-trace + +#include diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index b87404535c29..d57dc6cbd428 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -29,6 +29,7 @@ #include #include "xhci.h" +#include "xhci-trace.h" #define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" @@ -3666,7 +3667,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) union xhci_trb *cmd_trb; if (!udev->slot_id) { - xhci_dbg(xhci, "Bad Slot ID %d\n", udev->slot_id); + xhci_dbg_trace(xhci, trace_xhci_dbg_address, + "Bad Slot ID %d", udev->slot_id); return -EINVAL; } @@ -3712,7 +3714,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) udev->slot_id); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "FIXME: allocate a command ring segment\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_address, + "FIXME: allocate a command ring segment"); return ret; } xhci_ring_cmd_db(xhci); @@ -3752,7 +3755,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) ret = -ENODEV; break; case COMP_SUCCESS: - xhci_dbg(xhci, "Successful Address Device command\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_address, + "Successful Address Device command"); break; default: xhci_err(xhci, "ERROR: unexpected command completion " @@ -3766,13 +3770,16 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) return ret; } temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); - xhci_dbg(xhci, "Op regs DCBAA ptr = %#016llx\n", temp_64); - xhci_dbg(xhci, "Slot ID %d dcbaa entry @%p = %#016llx\n", - udev->slot_id, - &xhci->dcbaa->dev_context_ptrs[udev->slot_id], - (unsigned long long) - le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id])); - xhci_dbg(xhci, "Output Context DMA address = %#08llx\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_address, + "Op regs DCBAA ptr = %#016llx", temp_64); + xhci_dbg_trace(xhci, trace_xhci_dbg_address, + "Slot ID %d dcbaa entry @%p = %#016llx", + udev->slot_id, + &xhci->dcbaa->dev_context_ptrs[udev->slot_id], + (unsigned long long) + le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id])); + xhci_dbg_trace(xhci, trace_xhci_dbg_address, + "Output Context DMA address = %#08llx", (unsigned long long)virt_dev->out_ctx->dma); xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2); @@ -3791,7 +3798,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) ctrl_ctx->add_flags = 0; ctrl_ctx->drop_flags = 0; - xhci_dbg(xhci, "Internal device address = %d\n", virt_dev->address); + xhci_dbg_trace(xhci, trace_xhci_dbg_address, + "Internal device address = %d", virt_dev->address); return 0; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 4d43531f5fcf..d2045916531b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1647,6 +1647,8 @@ char *xhci_get_slot_state(struct xhci_hcd *xhci, void xhci_dbg_ep_rings(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, struct xhci_virt_ep *ep); +void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), + const char *fmt, ...); /* xHCI memory management */ void xhci_mem_cleanup(struct xhci_hcd *xhci); -- cgit v1.2.3-58-ga151 From 3a7fa5bef06e1757d1aded8d78afd041b4552e11 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Wed, 31 Jul 2013 07:35:27 +0300 Subject: xhci: add trace for debug messages related to changing contexts This patch defines a new trace event, which is called xhci_dbg_context_change and belongs in the event class xhci_log_msg, and adds tracepoints for tracing the debug messages related to context updates performed with Configure Endpoint and Evaluate Context commands. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 4 +++- drivers/usb/host/xhci-ring.c | 4 +++- drivers/usb/host/xhci-trace.h | 5 +++++ drivers/usb/host/xhci.c | 24 ++++++++++++++++-------- 4 files changed, 27 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index b881bc1697f9..b1bb59b58b25 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -26,6 +26,7 @@ #include #include "xhci.h" +#include "xhci-trace.h" /* * Allocates a generic ring segment from the ring pool, sets the dma address, @@ -661,7 +662,8 @@ void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci, * fls(0) = 0, fls(0x1) = 1, fls(0x10) = 2, fls(0x100) = 3, etc. */ max_primary_streams = fls(stream_info->num_stream_ctxs) - 2; - xhci_dbg(xhci, "Setting number of stream ctx array entries to %u\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Setting number of stream ctx array entries to %u", 1 << (max_primary_streams + 1)); ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK); ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5b08cd85f8e7..ba5fd5a8c9bc 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -67,6 +67,7 @@ #include #include #include "xhci.h" +#include "xhci-trace.h" static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, @@ -1454,7 +1455,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, break; } bandwidth_change: - xhci_dbg(xhci, "Completed config ep cmd\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Completed config ep cmd"); xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status)); complete(&xhci->devs[slot_id]->cmd_completion); diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 432aa675966c..628624306f38 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -36,6 +36,11 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_address, TP_ARGS(vaf) ); +DEFINE_EVENT(xhci_log_msg, xhci_dbg_context_change, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + #endif /* __XHCI_TRACE_H */ /* this part must be outside header guard */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index d57dc6cbd428..fa7b6f43642f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1153,12 +1153,16 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); max_packet_size = usb_endpoint_maxp(&urb->dev->ep0.desc); if (hw_max_packet_size != max_packet_size) { - xhci_dbg(xhci, "Max Packet Size for ep 0 changed.\n"); - xhci_dbg(xhci, "Max packet size in usb_device = %d\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Max Packet Size for ep 0 changed."); + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Max packet size in usb_device = %d", max_packet_size); - xhci_dbg(xhci, "Max packet size in xHCI HW = %d\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Max packet size in xHCI HW = %d", hw_max_packet_size); - xhci_dbg(xhci, "Issuing evaluate context command.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Issuing evaluate context command."); /* Set up the input context flags for the command */ /* FIXME: This won't work if a non-default control endpoint @@ -1776,7 +1780,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = -ENODEV; break; case COMP_SUCCESS: - dev_dbg(&udev->dev, "Successful Endpoint Configure command\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Successful Endpoint Configure command"); ret = 0; break; default: @@ -1822,7 +1827,8 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, ret = -EINVAL; break; case COMP_SUCCESS: - dev_dbg(&udev->dev, "Successful evaluate context command\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Successful evaluate context command"); ret = 0; break; default: @@ -2583,7 +2589,8 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) xhci_free_host_resources(xhci, ctrl_ctx); spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "FIXME allocate a new ring segment\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "FIXME allocate a new ring segment"); return -ENOMEM; } xhci_ring_cmd_db(xhci); @@ -3865,7 +3872,8 @@ static int __maybe_unused xhci_change_max_exit_latency(struct xhci_hcd *xhci, slot_ctx->dev_info2 &= cpu_to_le32(~((u32) MAX_EXIT)); slot_ctx->dev_info2 |= cpu_to_le32(max_exit_latency); - xhci_dbg(xhci, "Set up evaluate context for LPM MEL change.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, + "Set up evaluate context for LPM MEL change."); xhci_dbg(xhci, "Slot %u Input Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, command->in_ctx, 0); -- cgit v1.2.3-58-ga151 From 4bdfe4c38fd5b06da558050a3288a0a48825a3e7 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Tue, 6 Aug 2013 07:52:45 +0300 Subject: xhci: add trace for debug messages related to quirks This patch defines a new trace event, which is called xhci_dbg_quirks and belongs in the event class xhci_log_msg, and adds tracepoints that trace the debug messages associated with xHCs' quirks. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 7 +++-- drivers/usb/host/xhci-pci.c | 14 +++++---- drivers/usb/host/xhci-ring.c | 11 ++++--- drivers/usb/host/xhci-trace.h | 5 ++++ drivers/usb/host/xhci.c | 67 +++++++++++++++++++++++++++---------------- 5 files changed, 69 insertions(+), 35 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 93f3fdf0ff0a..fae697ed0b70 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -24,6 +24,7 @@ #include #include "xhci.h" +#include "xhci-trace.h" #define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E) #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ @@ -535,8 +536,10 @@ void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex) xhci->port_status_u0 |= 1 << wIndex; if (xhci->port_status_u0 == all_ports_seen_u0) { del_timer_sync(&xhci->comp_mode_recovery_timer); - xhci_dbg(xhci, "All USB3 ports have entered U0 already!\n"); - xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "All USB3 ports have entered U0 already!"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Compliance Mode Recovery Timer Deleted."); } } } diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 72960684a942..c2d495057eb5 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -25,6 +25,7 @@ #include #include "xhci.h" +#include "xhci-trace.h" /* Device for a quirk */ #define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 @@ -64,16 +65,18 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK && pdev->revision == 0x0) { xhci->quirks |= XHCI_RESET_EP_QUIRK; - xhci_dbg(xhci, "QUIRK: Fresco Logic xHC needs configure" - " endpoint cmd after reset endpoint\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "QUIRK: Fresco Logic xHC needs configure" + " endpoint cmd after reset endpoint"); } /* Fresco Logic confirms: all revisions of this chip do not * support MSI, even though some of them claim to in their PCI * capabilities. */ xhci->quirks |= XHCI_BROKEN_MSI; - xhci_dbg(xhci, "QUIRK: Fresco Logic revision %u " - "has broken MSI implementation\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "QUIRK: Fresco Logic revision %u " + "has broken MSI implementation", pdev->revision); xhci->quirks |= XHCI_TRUST_TX_LENGTH; } @@ -110,7 +113,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_ASROCK_P67) { xhci->quirks |= XHCI_RESET_ON_RESUME; - xhci_dbg(xhci, "QUIRK: Resetting on resume\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "QUIRK: Resetting on resume"); xhci->quirks |= XHCI_TRUST_TX_LENGTH; } if (pdev->vendor == PCI_VENDOR_ID_VIA) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index ba5fd5a8c9bc..e88e8772ab5d 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1159,7 +1159,8 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci, * because the HW can't handle two commands being queued in a row. */ if (xhci->quirks & XHCI_RESET_EP_QUIRK) { - xhci_dbg(xhci, "Queueing configure endpoint command\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Queueing configure endpoint command"); xhci_queue_configure_endpoint(xhci, xhci->devs[slot_id]->in_ctx->dma, slot_id, false); @@ -1445,8 +1446,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; if (!(ep_state & EP_HALTED)) goto bandwidth_change; - xhci_dbg(xhci, "Completed config ep cmd - " - "last ep index = %d, state = %d\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Completed config ep cmd - " + "last ep index = %d, state = %d", ep_index, ep_state); /* Clear internal halted state and restart ring(s) */ xhci->devs[slot_id]->eps[ep_index].ep_state &= @@ -1499,7 +1501,8 @@ bandwidth_change: xhci->error_bitmask |= 1 << 6; break; } - xhci_dbg(xhci, "NEC firmware version %2x.%02x\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "NEC firmware version %2x.%02x", NEC_FW_MAJOR(le32_to_cpu(event->status)), NEC_FW_MINOR(le32_to_cpu(event->status))); break; diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 628624306f38..f0447c6cb29b 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -41,6 +41,11 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_context_change, TP_ARGS(vaf) ); +DEFINE_EVENT(xhci_log_msg, xhci_dbg_quirks, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + #endif /* __XHCI_TRACE_H */ /* this part must be outside header guard */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index fa7b6f43642f..ffb3ed09eb0d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -418,9 +418,11 @@ static void compliance_mode_recovery(unsigned long arg) * Compliance Mode Detected. Letting USB Core * handle the Warm Reset */ - xhci_dbg(xhci, "Compliance mode detected->port %d\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Compliance mode detected->port %d", i + 1); - xhci_dbg(xhci, "Attempting compliance mode recovery\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Attempting compliance mode recovery"); hcd = xhci->shared_hcd; if (hcd->state == HC_STATE_SUSPENDED) @@ -458,7 +460,8 @@ static void compliance_mode_recovery_timer_init(struct xhci_hcd *xhci) set_timer_slack(&xhci->comp_mode_recovery_timer, msecs_to_jiffies(COMP_MODE_RCVRY_MSECS)); add_timer(&xhci->comp_mode_recovery_timer); - xhci_dbg(xhci, "Compliance mode recovery timer initialized\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Compliance mode recovery timer initialized"); } /* @@ -509,7 +512,8 @@ int xhci_init(struct usb_hcd *hcd) xhci_dbg(xhci, "xhci_init\n"); spin_lock_init(&xhci->lock); if (xhci->hci_version == 0x95 && link_quirk) { - xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "QUIRK: Not clearing Link TRB chain bits."); xhci->quirks |= XHCI_LINK_TRB_QUIRK; } else { xhci_dbg(xhci, "xHCI doesn't need link TRB QUIRK\n"); @@ -668,7 +672,8 @@ void xhci_stop(struct usb_hcd *hcd) if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && (!(xhci_all_ports_seen_u0(xhci)))) { del_timer_sync(&xhci->comp_mode_recovery_timer); - xhci_dbg(xhci, "%s: compliance mode recovery timer deleted\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "%s: compliance mode recovery timer deleted", __func__); } @@ -866,7 +871,8 @@ int xhci_suspend(struct xhci_hcd *xhci) if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && (!(xhci_all_ports_seen_u0(xhci)))) { del_timer_sync(&xhci->comp_mode_recovery_timer); - xhci_dbg(xhci, "%s: compliance mode recovery timer deleted\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "%s: compliance mode recovery timer deleted", __func__); } @@ -931,7 +937,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !(xhci_all_ports_seen_u0(xhci))) { del_timer_sync(&xhci->comp_mode_recovery_timer); - xhci_dbg(xhci, "Compliance Mode Recovery Timer deleted!\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Compliance Mode Recovery Timer deleted!"); } /* Let the USB core know _both_ roothubs lost power. */ @@ -1894,14 +1901,16 @@ static int xhci_reserve_host_resources(struct xhci_hcd *xhci, added_eps = xhci_count_num_new_endpoints(xhci, ctrl_ctx); if (xhci->num_active_eps + added_eps > xhci->limit_active_eps) { - xhci_dbg(xhci, "Not enough ep ctxs: " - "%u active, need to add %u, limit is %u.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Not enough ep ctxs: " + "%u active, need to add %u, limit is %u.", xhci->num_active_eps, added_eps, xhci->limit_active_eps); return -ENOMEM; } xhci->num_active_eps += added_eps; - xhci_dbg(xhci, "Adding %u ep ctxs, %u now active.\n", added_eps, + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Adding %u ep ctxs, %u now active.", added_eps, xhci->num_active_eps); return 0; } @@ -1919,7 +1928,8 @@ static void xhci_free_host_resources(struct xhci_hcd *xhci, num_failed_eps = xhci_count_num_new_endpoints(xhci, ctrl_ctx); xhci->num_active_eps -= num_failed_eps; - xhci_dbg(xhci, "Removing %u failed ep ctxs, %u now active.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Removing %u failed ep ctxs, %u now active.", num_failed_eps, xhci->num_active_eps); } @@ -1938,7 +1948,8 @@ static void xhci_finish_resource_reservation(struct xhci_hcd *xhci, num_dropped_eps = xhci_count_num_dropped_endpoints(xhci, ctrl_ctx); xhci->num_active_eps -= num_dropped_eps; if (num_dropped_eps) - xhci_dbg(xhci, "Removing %u dropped ep ctxs, %u now active.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Removing %u dropped ep ctxs, %u now active.", num_dropped_eps, xhci->num_active_eps); } @@ -2099,18 +2110,21 @@ static int xhci_check_bw_table(struct xhci_hcd *xhci, * that the HS bus has enough bandwidth if we are activing a new TT. */ if (virt_dev->tt_info) { - xhci_dbg(xhci, "Recalculating BW for rootport %u\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Recalculating BW for rootport %u", virt_dev->real_port); if (xhci_check_tt_bw_table(xhci, virt_dev, old_active_eps)) { xhci_warn(xhci, "Not enough bandwidth on HS bus for " "newly activated TT.\n"); return -ENOMEM; } - xhci_dbg(xhci, "Recalculating BW for TT slot %u port %u\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Recalculating BW for TT slot %u port %u", virt_dev->tt_info->slot_id, virt_dev->tt_info->ttport); } else { - xhci_dbg(xhci, "Recalculating BW for rootport %u\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Recalculating BW for rootport %u", virt_dev->real_port); } @@ -2218,8 +2232,9 @@ static int xhci_check_bw_table(struct xhci_hcd *xhci, xhci->rh_bw[port_index].num_active_tts; } - xhci_dbg(xhci, "Final bandwidth: %u, Limit: %u, Reserved: %u, " - "Available: %u " "percent\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Final bandwidth: %u, Limit: %u, Reserved: %u, " + "Available: %u " "percent", bw_used, max_bandwidth, bw_reserved, (max_bandwidth - bw_used - bw_reserved) * 100 / max_bandwidth); @@ -2825,8 +2840,9 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, * XXX: No idea how this hardware will react when stream rings * are enabled. */ - xhci_dbg(xhci, "Setting up input context for " - "configure endpoint command\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Setting up input context for " + "configure endpoint command"); xhci_setup_input_ctx_for_quirk(xhci, udev->slot_id, ep_index, &deq_state); } @@ -3305,8 +3321,9 @@ void xhci_free_device_endpoint_resources(struct xhci_hcd *xhci, } xhci->num_active_eps -= num_dropped_eps; if (num_dropped_eps) - xhci_dbg(xhci, "Dropped %u ep ctxs, flags = 0x%x, " - "%u now active.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Dropped %u ep ctxs, flags = 0x%x, " + "%u now active.", num_dropped_eps, drop_flags, xhci->num_active_eps); } @@ -3568,13 +3585,15 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) static int xhci_reserve_host_control_ep_resources(struct xhci_hcd *xhci) { if (xhci->num_active_eps + 1 > xhci->limit_active_eps) { - xhci_dbg(xhci, "Not enough ep ctxs: " - "%u active, need to add 1, limit is %u.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Not enough ep ctxs: " + "%u active, need to add 1, limit is %u.", xhci->num_active_eps, xhci->limit_active_eps); return -ENOMEM; } xhci->num_active_eps += 1; - xhci_dbg(xhci, "Adding 1 ep ctx, %u now active.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Adding 1 ep ctx, %u now active.", xhci->num_active_eps); return 0; } -- cgit v1.2.3-58-ga151 From a0254324eeea3b00676eeb54ba80774a46cb8afb Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Tue, 6 Aug 2013 07:52:46 +0300 Subject: xhci: add trace for debug messages related to endpoint reset This patch defines a new trace event, which is called xhci_dbg_reset_ep and belongs in the event class xhci_log_msg, and adds tracepoints that trace the debug messages associated with resetting an endpoint after the reception of a STALL packet. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 3 ++- drivers/usb/host/xhci-trace.h | 5 +++++ drivers/usb/host/xhci.c | 17 +++++++++++------ 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e88e8772ab5d..3393ce755b15 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1151,7 +1151,8 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci, /* This command will only fail if the endpoint wasn't halted, * but we don't care. */ - xhci_dbg(xhci, "Ignoring reset ep completion code of %u\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, + "Ignoring reset ep completion code of %u", GET_COMP_CODE(le32_to_cpu(event->status))); /* HW with the reset endpoint quirk needs to have a configure endpoint diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index f0447c6cb29b..757b26f6e37b 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -46,6 +46,11 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_quirks, TP_ARGS(vaf) ); +DEFINE_EVENT(xhci_log_msg, xhci_dbg_reset_ep, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + #endif /* __XHCI_TRACE_H */ /* this part must be outside header guard */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ffb3ed09eb0d..18c710dc68dc 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2818,7 +2818,8 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, struct xhci_dequeue_state deq_state; struct xhci_virt_ep *ep; - xhci_dbg(xhci, "Cleaning up stalled endpoint ring\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, + "Cleaning up stalled endpoint ring"); ep = &xhci->devs[udev->slot_id]->eps[ep_index]; /* We need to move the HW's dequeue pointer past this TD, * or it will attempt to resend it on the next doorbell ring. @@ -2831,7 +2832,8 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, * issue a configure endpoint command later. */ if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) { - xhci_dbg(xhci, "Queueing new dequeue state\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, + "Queueing new dequeue state"); xhci_queue_new_dequeue_state(xhci, udev->slot_id, ep_index, ep->stopped_stream, &deq_state); } else { @@ -2874,16 +2876,19 @@ void xhci_endpoint_reset(struct usb_hcd *hcd, ep_index = xhci_get_endpoint_index(&ep->desc); virt_ep = &xhci->devs[udev->slot_id]->eps[ep_index]; if (!virt_ep->stopped_td) { - xhci_dbg(xhci, "Endpoint 0x%x not halted, refusing to reset.\n", - ep->desc.bEndpointAddress); + xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, + "Endpoint 0x%x not halted, refusing to reset.", + ep->desc.bEndpointAddress); return; } if (usb_endpoint_xfer_control(&ep->desc)) { - xhci_dbg(xhci, "Control endpoint stall already handled.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, + "Control endpoint stall already handled."); return; } - xhci_dbg(xhci, "Queueing reset endpoint command\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, + "Queueing reset endpoint command"); spin_lock_irqsave(&xhci->lock, flags); ret = xhci_queue_reset_ep(xhci, udev->slot_id, ep_index); /* -- cgit v1.2.3-58-ga151 From 1d27fabec068a204186c6af10e05f23911c0c902 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Tue, 6 Aug 2013 07:52:47 +0300 Subject: xhci: add xhci_address_ctx trace event This patch defines a new event class, called xhci_log_ctx, that records in the ring buffer the context data, the context type (input or output), the context dma and virtual addresses, the context endpoint entries, the slot ID and whether the xHC uses 64 byte context data structures. This information can be used, later, to parse and display the context data fields with the appropriate plugin using the trace-cmd tool. Also, this patch defines a trace event, called xhci_address_ctx, to trace the contexts related to the Address Device command and adds the associated tracepoints in xhci_address_device(). Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-trace.h | 42 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.c | 7 +++++++ 2 files changed, 49 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 757b26f6e37b..c653ddfc0211 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -18,6 +18,7 @@ #define __XHCI_TRACE_H #include +#include "xhci.h" #define XHCI_MSG_MAX 500 @@ -51,6 +52,47 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_reset_ep, TP_ARGS(vaf) ); +DECLARE_EVENT_CLASS(xhci_log_ctx, + TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, + unsigned int ep_num), + TP_ARGS(xhci, ctx, ep_num), + TP_STRUCT__entry( + __field(int, ctx_64) + __field(unsigned, ctx_type) + __field(dma_addr_t, ctx_dma) + __field(u8 *, ctx_va) + __field(unsigned, ctx_ep_num) + __field(int, slot_id) + __dynamic_array(u32, ctx_data, + ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) * + ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1)) + ), + TP_fast_assign( + struct usb_device *udev; + + udev = to_usb_device(xhci_to_hcd(xhci)->self.controller); + __entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params); + __entry->ctx_type = ctx->type; + __entry->ctx_dma = ctx->dma; + __entry->ctx_va = ctx->bytes; + __entry->slot_id = udev->slot_id; + __entry->ctx_ep_num = ep_num; + memcpy(__get_dynamic_array(ctx_data), ctx->bytes, + ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) * + ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1)); + ), + TP_printk("\nctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p", + __entry->ctx_64, __entry->ctx_type, + (unsigned long long) __entry->ctx_dma, __entry->ctx_va + ) +); + +DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx, + TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, + unsigned int ep_num), + TP_ARGS(xhci, ctx, ep_num) +); + #endif /* __XHCI_TRACE_H */ /* this part must be outside header guard */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 18c710dc68dc..9f4f73bfd01b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3738,6 +3738,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2); + trace_xhci_address_ctx(xhci, virt_dev->in_ctx, + slot_ctx->dev_info >> 27); spin_lock_irqsave(&xhci->lock, flags); cmd_trb = xhci->cmd_ring->dequeue; @@ -3794,6 +3796,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) "code 0x%x.\n", virt_dev->cmd_status); xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2); + trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1); ret = -EINVAL; break; } @@ -3814,6 +3817,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) (unsigned long long)virt_dev->out_ctx->dma); xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2); + trace_xhci_address_ctx(xhci, virt_dev->in_ctx, + slot_ctx->dev_info >> 27); xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2); /* @@ -3821,6 +3826,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) * address given back to us by the HC. */ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); + trace_xhci_address_ctx(xhci, virt_dev->out_ctx, + slot_ctx->dev_info >> 27); /* Use kernel assigned address for devices; store xHC assigned * address locally. */ virt_dev->address = (le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK) -- cgit v1.2.3-58-ga151 From 63a23b9a7451660525c90b08219e14e701e294f1 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Tue, 6 Aug 2013 07:52:48 +0300 Subject: xhci: add xhci_cmd_completion trace event This patch creates a new event class, called xhci_log_event, and defines the xhci_cmd_completion trace event used for tracing the commands issued to xHC that generate a completion event in the event ring. This info can be used, later, to print, in a human readable way, the completion status and flags as well as the command's type and fields using the trace-cmd tool and the appropriate plugin. Also, a tracepoint is added in handle_cmd_completion(). Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 3 +++ drivers/usb/host/xhci-trace.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3393ce755b15..86971ac5851b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1380,6 +1380,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, return; } + trace_xhci_cmd_completion(&xhci->cmd_ring->dequeue->generic, + (struct xhci_generic_trb *) event); + if ((GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_ABORT) || (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_STOP)) { /* If the return value is 0, we think the trb pointed by diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index c653ddfc0211..d6c222916858 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -93,6 +93,36 @@ DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx, TP_ARGS(xhci, ctx, ep_num) ); +DECLARE_EVENT_CLASS(xhci_log_event, + TP_PROTO(void *trb_va, struct xhci_generic_trb *ev), + TP_ARGS(trb_va, ev), + TP_STRUCT__entry( + __field(void *, va) + __field(u64, dma) + __field(u32, status) + __field(u32, flags) + __dynamic_array(__le32, trb, 4) + ), + TP_fast_assign( + __entry->va = trb_va; + __entry->dma = le64_to_cpu(((u64)ev->field[1]) << 32 | + ev->field[0]); + __entry->status = le32_to_cpu(ev->field[2]); + __entry->flags = le32_to_cpu(ev->field[3]); + memcpy(__get_dynamic_array(trb), trb_va, + sizeof(struct xhci_generic_trb)); + ), + TP_printk("\ntrb_dma=@%llx, trb_va=@%p, status=%08x, flags=%08x", + (unsigned long long) __entry->dma, __entry->va, + __entry->status, __entry->flags + ) +); + +DEFINE_EVENT(xhci_log_event, xhci_cmd_completion, + TP_PROTO(void *trb_va, struct xhci_generic_trb *ev), + TP_ARGS(trb_va, ev) +); + #endif /* __XHCI_TRACE_H */ /* this part must be outside header guard */ -- cgit v1.2.3-58-ga151 From aa50b29061d3df896c494d92e9c8c2e1f295cc6e Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Wed, 14 Aug 2013 06:33:54 +0300 Subject: xhci: trace debug statements for urb cancellation This patch defines a new trace event, which is called xhci_dbg_cancel_urb and belongs to the event class xhci_log_msg, and adds tracepoints that trace the debug messages related to the removal of a cancelled URB from the endpoint's transfer ring. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 64 +++++++++++++++++++++++++++---------------- drivers/usb/host/xhci-trace.h | 5 ++++ drivers/usb/host/xhci.c | 13 +++++---- 3 files changed, 54 insertions(+), 28 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 86971ac5851b..f908e205f7d5 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -556,7 +556,8 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, return; } state->new_cycle_state = 0; - xhci_dbg(xhci, "Finding segment containing stopped TRB.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Finding segment containing stopped TRB."); state->new_deq_seg = find_trb_seg(cur_td->start_seg, dev->eps[ep_index].stopped_trb, &state->new_cycle_state); @@ -566,12 +567,14 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, } /* Dig out the cycle state saved by the xHC during the stop ep cmd */ - xhci_dbg(xhci, "Finding endpoint context\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Finding endpoint context"); ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq); state->new_deq_ptr = cur_td->last_trb; - xhci_dbg(xhci, "Finding segment containing last TRB in TD.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Finding segment containing last TRB in TD."); state->new_deq_seg = find_trb_seg(state->new_deq_seg, state->new_deq_ptr, &state->new_cycle_state); @@ -598,13 +601,16 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, if (ep_ring->first_seg == ep_ring->first_seg->next && state->new_deq_ptr < dev->eps[ep_index].stopped_trb) state->new_cycle_state ^= 0x1; - xhci_dbg(xhci, "Cycle state = 0x%x\n", state->new_cycle_state); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Cycle state = 0x%x", state->new_cycle_state); /* Don't update the ring cycle state for the producer (us). */ - xhci_dbg(xhci, "New dequeue segment = %p (virtual)\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "New dequeue segment = %p (virtual)", state->new_deq_seg); addr = xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr); - xhci_dbg(xhci, "New dequeue pointer = 0x%llx (DMA)\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "New dequeue pointer = 0x%llx (DMA)", (unsigned long long) addr); } @@ -632,9 +638,11 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, if (flip_cycle) cur_trb->generic.field[3] ^= cpu_to_le32(TRB_CYCLE); - xhci_dbg(xhci, "Cancel (unchain) link TRB\n"); - xhci_dbg(xhci, "Address = %p (0x%llx dma); " - "in seg %p (0x%llx dma)\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Cancel (unchain) link TRB"); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Address = %p (0x%llx dma); " + "in seg %p (0x%llx dma)", cur_trb, (unsigned long long)xhci_trb_virt_to_dma(cur_seg, cur_trb), cur_seg, @@ -652,7 +660,8 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, cpu_to_le32(TRB_CYCLE); cur_trb->generic.field[3] |= cpu_to_le32( TRB_TYPE(TRB_TR_NOOP)); - xhci_dbg(xhci, "TRB to noop at offset 0x%llx\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "TRB to noop at offset 0x%llx", (unsigned long long) xhci_trb_virt_to_dma(cur_seg, cur_trb)); } @@ -673,8 +682,9 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, { struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; - xhci_dbg(xhci, "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), " - "new deq ptr = %p (0x%llx dma), new cycle = %u\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), " + "new deq ptr = %p (0x%llx dma), new cycle = %u", deq_state->new_deq_seg, (unsigned long long)deq_state->new_deq_seg->dma, deq_state->new_deq_ptr, @@ -794,7 +804,8 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, */ list_for_each(entry, &ep->cancelled_td_list) { cur_td = list_entry(entry, struct xhci_td, cancelled_td_list); - xhci_dbg(xhci, "Removing canceled TD starting at 0x%llx (dma).\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Removing canceled TD starting at 0x%llx (dma).", (unsigned long long)xhci_trb_virt_to_dma( cur_td->start_seg, cur_td->first_trb)); ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb); @@ -914,14 +925,16 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) ep->stop_cmds_pending--; if (xhci->xhc_state & XHCI_STATE_DYING) { - xhci_dbg(xhci, "Stop EP timer ran, but another timer marked " - "xHCI as DYING, exiting.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Stop EP timer ran, but another timer marked " + "xHCI as DYING, exiting."); spin_unlock_irqrestore(&xhci->lock, flags); return; } if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) { - xhci_dbg(xhci, "Stop EP timer ran, but no command pending, " - "exiting.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Stop EP timer ran, but no command pending, " + "exiting."); spin_unlock_irqrestore(&xhci->lock, flags); return; } @@ -963,8 +976,9 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) ring = temp_ep->ring; if (!ring) continue; - xhci_dbg(xhci, "Killing URBs for slot ID %u, " - "ep index %u\n", i, j); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Killing URBs for slot ID %u, " + "ep index %u", i, j); while (!list_empty(&ring->td_list)) { cur_td = list_first_entry(&ring->td_list, struct xhci_td, @@ -987,9 +1001,11 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) } } spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "Calling usb_hc_died()\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Calling usb_hc_died()"); usb_hc_died(xhci_to_hcd(xhci)->primary_hcd); - xhci_dbg(xhci, "xHCI host controller is dead.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "xHCI host controller is dead."); } @@ -1093,7 +1109,8 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, ep_state &= EP_STATE_MASK; slot_state = le32_to_cpu(slot_ctx->dev_state); slot_state = GET_SLOT_STATE(slot_state); - xhci_dbg(xhci, "Slot state = %u, EP state = %u\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Slot state = %u, EP state = %u", slot_state, ep_state); break; case COMP_EBADSLT: @@ -1113,7 +1130,8 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, * cancelling URBs, which might not be an error... */ } else { - xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq = @%08llx\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Successful Set TR Deq Ptr cmd, deq = @%08llx", le64_to_cpu(ep_ctx->deq)); if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg, dev->eps[ep_index].queued_deq_ptr) == diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index d6c222916858..c0eaccc10a3c 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -52,6 +52,11 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_reset_ep, TP_ARGS(vaf) ); +DEFINE_EVENT(xhci_log_msg, xhci_dbg_cancel_urb, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + DECLARE_EVENT_CLASS(xhci_log_ctx, TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int ep_num), diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 9f4f73bfd01b..4adf5e19918a 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1434,7 +1434,8 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto done; temp = xhci_readl(xhci, &xhci->op_regs->status); if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) { - xhci_dbg(xhci, "HW died, freeing TD.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "HW died, freeing TD."); urb_priv = urb->hcpriv; for (i = urb_priv->td_cnt; i < urb_priv->length; i++) { td = urb_priv->td[i]; @@ -1452,8 +1453,9 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } if ((xhci->xhc_state & XHCI_STATE_DYING) || (xhci->xhc_state & XHCI_STATE_HALTED)) { - xhci_dbg(xhci, "Ep 0x%x: URB %p to be canceled on " - "non-responsive xHCI host.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Ep 0x%x: URB %p to be canceled on " + "non-responsive xHCI host.", urb->ep->desc.bEndpointAddress, urb); /* Let the stop endpoint command watchdog timer (which set this * state) finish cleaning up the endpoint TD lists. We must @@ -1474,8 +1476,9 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) urb_priv = urb->hcpriv; i = urb_priv->td_cnt; if (i < urb_priv->length) - xhci_dbg(xhci, "Cancel URB %p, dev %s, ep 0x%x, " - "starting at offset 0x%llx\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Cancel URB %p, dev %s, ep 0x%x, " + "starting at offset 0x%llx", urb, urb->dev->devpath, urb->ep->desc.bEndpointAddress, (unsigned long long) xhci_trb_virt_to_dma( -- cgit v1.2.3-58-ga151 From d195fcffe4c82cc813cc43df5f27ab99ab38bc07 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Wed, 14 Aug 2013 06:33:55 +0300 Subject: xhci: trace debug messages related to driver initialization and unload This patch defines a new trace event, which is called xhci_dbg_init and belongs to the event class xhci_log_msg, and adds tracepoints that trace the debug statements in the functions used to start and stop the xhci-hcd driver. Also, it removes an unnecessary cast of variable val to unsigned int in xhci_mem_init(), since val is already declared as unsigned int. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 110 ++++++++++++++++++++++++++---------------- drivers/usb/host/xhci-trace.h | 5 ++ drivers/usb/host/xhci.c | 66 +++++++++++++++---------- 3 files changed, 113 insertions(+), 68 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index b1bb59b58b25..ef27470b99e3 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1545,7 +1545,8 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) struct device *dev = xhci_to_hcd(xhci)->self.controller; int num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2); - xhci_dbg(xhci, "Allocating %d scratchpad buffers\n", num_sp); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Allocating %d scratchpad buffers", num_sp); if (!num_sp) return 0; @@ -1702,11 +1703,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) dma_free_coherent(&pdev->dev, size, xhci->erst.entries, xhci->erst.erst_dma_addr); xhci->erst.entries = NULL; - xhci_dbg(xhci, "Freed ERST\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed ERST"); if (xhci->event_ring) xhci_ring_free(xhci, xhci->event_ring); xhci->event_ring = NULL; - xhci_dbg(xhci, "Freed event ring\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring"); if (xhci->lpm_command) xhci_free_command(xhci, xhci->lpm_command); @@ -1714,7 +1715,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); xhci->cmd_ring = NULL; - xhci_dbg(xhci, "Freed command ring\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring"); list_for_each_entry_safe(cur_cd, next_cd, &xhci->cancel_cmd_list, cancel_cmd_list) { list_del(&cur_cd->cancel_cmd_list); @@ -1727,22 +1728,24 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) if (xhci->segment_pool) dma_pool_destroy(xhci->segment_pool); xhci->segment_pool = NULL; - xhci_dbg(xhci, "Freed segment pool\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed segment pool"); if (xhci->device_pool) dma_pool_destroy(xhci->device_pool); xhci->device_pool = NULL; - xhci_dbg(xhci, "Freed device context pool\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed device context pool"); if (xhci->small_streams_pool) dma_pool_destroy(xhci->small_streams_pool); xhci->small_streams_pool = NULL; - xhci_dbg(xhci, "Freed small stream array pool\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Freed small stream array pool"); if (xhci->medium_streams_pool) dma_pool_destroy(xhci->medium_streams_pool); xhci->medium_streams_pool = NULL; - xhci_dbg(xhci, "Freed medium stream array pool\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Freed medium stream array pool"); if (xhci->dcbaa) dma_free_coherent(&pdev->dev, sizeof(*xhci->dcbaa), @@ -1968,8 +1971,9 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) * there might be more events to service. */ temp &= ~ERST_EHB; - xhci_dbg(xhci, "// Write event ring dequeue pointer, " - "preserving EHB bit\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Write event ring dequeue pointer, " + "preserving EHB bit"); xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, &xhci->ir_set->erst_dequeue); } @@ -1992,8 +1996,9 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, temp = xhci_readl(xhci, addr + 2); port_offset = XHCI_EXT_PORT_OFF(temp); port_count = XHCI_EXT_PORT_COUNT(temp); - xhci_dbg(xhci, "Ext Cap %p, port offset = %u, " - "count = %u, revision = 0x%x\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Ext Cap %p, port offset = %u, " + "count = %u, revision = 0x%x", addr, port_offset, port_count, major_revision); /* Port count includes the current port offset */ if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) @@ -2007,15 +2012,18 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, /* Check the host's USB2 LPM capability */ if ((xhci->hci_version == 0x96) && (major_revision != 0x03) && (temp & XHCI_L1C)) { - xhci_dbg(xhci, "xHCI 0.96: support USB2 software lpm\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "xHCI 0.96: support USB2 software lpm"); xhci->sw_lpm_support = 1; } if ((xhci->hci_version >= 0x100) && (major_revision != 0x03)) { - xhci_dbg(xhci, "xHCI 1.0: support USB2 software lpm\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "xHCI 1.0: support USB2 software lpm"); xhci->sw_lpm_support = 1; if (temp & XHCI_HLC) { - xhci_dbg(xhci, "xHCI 1.0: support USB2 hardware lpm\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "xHCI 1.0: support USB2 hardware lpm"); xhci->hw_lpm_support = 1; } } @@ -2139,18 +2147,21 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci_warn(xhci, "No ports on the roothubs?\n"); return -ENODEV; } - xhci_dbg(xhci, "Found %u USB 2.0 ports and %u USB 3.0 ports.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Found %u USB 2.0 ports and %u USB 3.0 ports.", xhci->num_usb2_ports, xhci->num_usb3_ports); /* Place limits on the number of roothub ports so that the hub * descriptors aren't longer than the USB core will allocate. */ if (xhci->num_usb3_ports > 15) { - xhci_dbg(xhci, "Limiting USB 3.0 roothub ports to 15.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Limiting USB 3.0 roothub ports to 15."); xhci->num_usb3_ports = 15; } if (xhci->num_usb2_ports > USB_MAXCHILDREN) { - xhci_dbg(xhci, "Limiting USB 2.0 roothub ports to %u.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Limiting USB 2.0 roothub ports to %u.", USB_MAXCHILDREN); xhci->num_usb2_ports = USB_MAXCHILDREN; } @@ -2175,8 +2186,9 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci->usb2_ports[port_index] = &xhci->op_regs->port_status_base + NUM_PORT_REGS*i; - xhci_dbg(xhci, "USB 2.0 port at index %u, " - "addr = %p\n", i, + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "USB 2.0 port at index %u, " + "addr = %p", i, xhci->usb2_ports[port_index]); port_index++; if (port_index == xhci->num_usb2_ports) @@ -2195,8 +2207,9 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci->usb3_ports[port_index] = &xhci->op_regs->port_status_base + NUM_PORT_REGS*i; - xhci_dbg(xhci, "USB 3.0 port at index %u, " - "addr = %p\n", i, + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "USB 3.0 port at index %u, " + "addr = %p", i, xhci->usb3_ports[port_index]); port_index++; if (port_index == xhci->num_usb3_ports) @@ -2220,32 +2233,35 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) INIT_LIST_HEAD(&xhci->cancel_cmd_list); page_size = xhci_readl(xhci, &xhci->op_regs->page_size); - xhci_dbg(xhci, "Supported page size register = 0x%x\n", page_size); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Supported page size register = 0x%x", page_size); for (i = 0; i < 16; i++) { if ((0x1 & page_size) != 0) break; page_size = page_size >> 1; } if (i < 16) - xhci_dbg(xhci, "Supported page size of %iK\n", (1 << (i+12)) / 1024); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Supported page size of %iK", (1 << (i+12)) / 1024); else xhci_warn(xhci, "WARN: no supported page size\n"); /* Use 4K pages, since that's common and the minimum the HC supports */ xhci->page_shift = 12; xhci->page_size = 1 << xhci->page_shift; - xhci_dbg(xhci, "HCD page size set to %iK\n", xhci->page_size / 1024); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "HCD page size set to %iK", xhci->page_size / 1024); /* * Program the Number of Device Slots Enabled field in the CONFIG * register with the max value of slots the HC can handle. */ val = HCS_MAX_SLOTS(xhci_readl(xhci, &xhci->cap_regs->hcs_params1)); - xhci_dbg(xhci, "// xHC can handle at most %d device slots.\n", - (unsigned int) val); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// xHC can handle at most %d device slots.", val); val2 = xhci_readl(xhci, &xhci->op_regs->config_reg); val |= (val2 & ~HCS_SLOTS_MASK); - xhci_dbg(xhci, "// Setting Max device slots reg = 0x%x.\n", - (unsigned int) val); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Setting Max device slots reg = 0x%x.", val); xhci_writel(xhci, val, &xhci->op_regs->config_reg); /* @@ -2258,7 +2274,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) goto fail; memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa)); xhci->dcbaa->dma = dma; - xhci_dbg(xhci, "// Device context base array address = 0x%llx (DMA), %p (virt)\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Device context base array address = 0x%llx (DMA), %p (virt)", (unsigned long long)xhci->dcbaa->dma, xhci->dcbaa); xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr); @@ -2297,8 +2314,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, flags); if (!xhci->cmd_ring) goto fail; - xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring); - xhci_dbg(xhci, "First segment DMA is 0x%llx\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Allocated command ring at %p", xhci->cmd_ring); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%llx", (unsigned long long)xhci->cmd_ring->first_seg->dma); /* Set the address in the Command Ring Control register */ @@ -2306,7 +2324,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) | (xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) | xhci->cmd_ring->cycle_state; - xhci_dbg(xhci, "// Setting command ring address to 0x%x\n", val); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Setting command ring address to 0x%x", val); xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); xhci_dbg_cmd_ptrs(xhci); @@ -2322,8 +2341,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) val = xhci_readl(xhci, &xhci->cap_regs->db_off); val &= DBOFF_MASK; - xhci_dbg(xhci, "// Doorbell array is located at offset 0x%x" - " from cap regs base addr\n", val); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Doorbell array is located at offset 0x%x" + " from cap regs base addr", val); xhci->dba = (void __iomem *) xhci->cap_regs + val; xhci_dbg_regs(xhci); xhci_print_run_regs(xhci); @@ -2334,7 +2354,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * Event ring setup: Allocate a normal ring, but also setup * the event ring segment table (ERST). Section 4.9.3. */ - xhci_dbg(xhci, "// Allocating event ring\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring"); xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, flags); if (!xhci->event_ring) @@ -2347,13 +2367,15 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) GFP_KERNEL); if (!xhci->erst.entries) goto fail; - xhci_dbg(xhci, "// Allocated event ring segment table at 0x%llx\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Allocated event ring segment table at 0x%llx", (unsigned long long)dma); memset(xhci->erst.entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS); xhci->erst.num_entries = ERST_NUM_SEGS; xhci->erst.erst_dma_addr = dma; - xhci_dbg(xhci, "Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx", xhci->erst.num_entries, xhci->erst.entries, (unsigned long long)xhci->erst.erst_dma_addr); @@ -2371,13 +2393,16 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) val = xhci_readl(xhci, &xhci->ir_set->erst_size); val &= ERST_SIZE_MASK; val |= ERST_NUM_SEGS; - xhci_dbg(xhci, "// Write ERST size = %i to ir_set 0 (some bits preserved)\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Write ERST size = %i to ir_set 0 (some bits preserved)", val); xhci_writel(xhci, val, &xhci->ir_set->erst_size); - xhci_dbg(xhci, "// Set ERST entries to point to event ring.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Set ERST entries to point to event ring."); /* set the segment table base address */ - xhci_dbg(xhci, "// Set ERST base address for ir_set 0 = 0x%llx\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Set ERST base address for ir_set 0 = 0x%llx", (unsigned long long)xhci->erst.erst_dma_addr); val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); val_64 &= ERST_PTR_MASK; @@ -2386,7 +2411,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* Set the event ring dequeue address */ xhci_set_hc_event_deq(xhci); - xhci_dbg(xhci, "Wrote ERST address to ir_set 0.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Wrote ERST address to ir_set 0."); xhci_print_ir_set(xhci, 0); /* diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index c0eaccc10a3c..11025fe6ebba 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -57,6 +57,11 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_cancel_urb, TP_ARGS(vaf) ); +DEFINE_EVENT(xhci_log_msg, xhci_dbg_init, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + DECLARE_EVENT_CLASS(xhci_log_ctx, TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int ep_num), diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 4adf5e19918a..eeb94d109059 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -101,7 +101,7 @@ void xhci_quiesce(struct xhci_hcd *xhci) int xhci_halt(struct xhci_hcd *xhci) { int ret; - xhci_dbg(xhci, "// Halt the HC\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Halt the HC"); xhci_quiesce(xhci); ret = xhci_handshake(xhci, &xhci->op_regs->status, @@ -125,7 +125,7 @@ static int xhci_start(struct xhci_hcd *xhci) temp = xhci_readl(xhci, &xhci->op_regs->command); temp |= (CMD_RUN); - xhci_dbg(xhci, "// Turn on HC, cmd = 0x%x.\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Turn on HC, cmd = 0x%x.", temp); xhci_writel(xhci, temp, &xhci->op_regs->command); @@ -163,7 +163,7 @@ int xhci_reset(struct xhci_hcd *xhci) return 0; } - xhci_dbg(xhci, "// Reset the HC\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Reset the HC"); command = xhci_readl(xhci, &xhci->op_regs->command); command |= CMD_RESET; xhci_writel(xhci, command, &xhci->op_regs->command); @@ -173,7 +173,8 @@ int xhci_reset(struct xhci_hcd *xhci) if (ret) return ret; - xhci_dbg(xhci, "Wait for controller to be ready for doorbell rings\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Wait for controller to be ready for doorbell rings"); /* * xHCI cannot write to any doorbells or operational registers other * than status until the "Controller Not Ready" flag is cleared. @@ -215,14 +216,16 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) ret = pci_enable_msi(pdev); if (ret) { - xhci_dbg(xhci, "failed to allocate MSI entry\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "failed to allocate MSI entry"); return ret; } ret = request_irq(pdev->irq, xhci_msi_irq, 0, "xhci_hcd", xhci_to_hcd(xhci)); if (ret) { - xhci_dbg(xhci, "disable MSI interrupt\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "disable MSI interrupt"); pci_disable_msi(pdev); } @@ -285,7 +288,8 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count); if (ret) { - xhci_dbg(xhci, "Failed to enable MSI-X\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Failed to enable MSI-X"); goto free_entries; } @@ -301,7 +305,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) return ret; disable_msix: - xhci_dbg(xhci, "disable MSI-X interrupt\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt"); xhci_free_irq(xhci); pci_disable_msix(pdev); free_entries: @@ -509,17 +513,18 @@ int xhci_init(struct usb_hcd *hcd) struct xhci_hcd *xhci = hcd_to_xhci(hcd); int retval = 0; - xhci_dbg(xhci, "xhci_init\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_init"); spin_lock_init(&xhci->lock); if (xhci->hci_version == 0x95 && link_quirk) { xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "QUIRK: Not clearing Link TRB chain bits."); xhci->quirks |= XHCI_LINK_TRB_QUIRK; } else { - xhci_dbg(xhci, "xHCI doesn't need link TRB QUIRK\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "xHCI doesn't need link TRB QUIRK"); } retval = xhci_mem_init(xhci, GFP_KERNEL); - xhci_dbg(xhci, "Finished xhci_init\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_init"); /* Initializing Compliance Mode Recovery Data If Needed */ if (xhci_compliance_mode_recovery_timer_quirk_check()) { @@ -545,7 +550,8 @@ static int xhci_run_finished(struct xhci_hcd *xhci) if (xhci->quirks & XHCI_NEC_HOST) xhci_ring_cmd_db(xhci); - xhci_dbg(xhci, "Finished xhci_run for USB3 roothub\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Finished xhci_run for USB3 roothub"); return 0; } @@ -576,7 +582,7 @@ int xhci_run(struct usb_hcd *hcd) if (!usb_hcd_is_primary_hcd(hcd)) return xhci_run_finished(xhci); - xhci_dbg(xhci, "xhci_run\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_run"); ret = xhci_try_enable_msi(hcd); if (ret) @@ -594,9 +600,11 @@ int xhci_run(struct usb_hcd *hcd) xhci_dbg_ring_ptrs(xhci, xhci->event_ring); temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); temp_64 &= ~ERST_PTR_MASK; - xhci_dbg(xhci, "ERST deq = 64'h%0lx\n", (long unsigned int) temp_64); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "ERST deq = 64'h%0lx", (long unsigned int) temp_64); - xhci_dbg(xhci, "// Set the interrupt modulation register\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Set the interrupt modulation register"); temp = xhci_readl(xhci, &xhci->ir_set->irq_control); temp &= ~ER_IRQ_INTERVAL_MASK; temp |= (u32) 160; @@ -605,12 +613,13 @@ int xhci_run(struct usb_hcd *hcd) /* Set the HCD state before we enable the irqs */ temp = xhci_readl(xhci, &xhci->op_regs->command); temp |= (CMD_EIE); - xhci_dbg(xhci, "// Enable interrupts, cmd = 0x%x.\n", - temp); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Enable interrupts, cmd = 0x%x.", temp); xhci_writel(xhci, temp, &xhci->op_regs->command); temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); - xhci_dbg(xhci, "// Enabling event ring interrupter %p by writing 0x%x to irq_pending\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Enabling event ring interrupter %p by writing 0x%x to irq_pending", xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp)); xhci_writel(xhci, ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); @@ -620,7 +629,8 @@ int xhci_run(struct usb_hcd *hcd) xhci_queue_vendor_command(xhci, 0, 0, 0, TRB_TYPE(TRB_NEC_GET_FW)); - xhci_dbg(xhci, "Finished xhci_run for USB2 roothub\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Finished xhci_run for USB2 roothub"); return 0; } @@ -680,7 +690,8 @@ void xhci_stop(struct usb_hcd *hcd) if (xhci->quirks & XHCI_AMD_PLL_FIX) usb_amd_dev_put(); - xhci_dbg(xhci, "// Disabling event ring interrupts\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Disabling event ring interrupts"); temp = xhci_readl(xhci, &xhci->op_regs->status); xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status); temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); @@ -688,10 +699,11 @@ void xhci_stop(struct usb_hcd *hcd) &xhci->ir_set->irq_pending); xhci_print_ir_set(xhci, 0); - xhci_dbg(xhci, "cleaning up memory\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); - xhci_dbg(xhci, "xhci_stop completed - status = %x\n", - xhci_readl(xhci, &xhci->op_regs->status)); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "xhci_stop completed - status = %x", + xhci_readl(xhci, &xhci->op_regs->status)); } /* @@ -716,8 +728,9 @@ void xhci_shutdown(struct usb_hcd *hcd) xhci_cleanup_msix(xhci); - xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n", - xhci_readl(xhci, &xhci->op_regs->status)); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "xhci_shutdown completed - status = %x", + xhci_readl(xhci, &xhci->op_regs->status)); } #ifdef CONFIG_PM @@ -758,7 +771,8 @@ static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) xhci->cmd_ring->dequeue) & (u64) ~CMD_RING_RSVD_BITS) | xhci->cmd_ring->cycle_state; - xhci_dbg(xhci, "// Setting command ring address to 0x%llx\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Setting command ring address to 0x%llx", (long unsigned long) val_64); xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); } -- cgit v1.2.3-58-ga151 From 68ffb01111500c769ceac055bdc2fb000322b2d7 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Wed, 14 Aug 2013 06:33:56 +0300 Subject: xhci: trace debug statements related to ring expansion This patch defines a new trace event, which is called xhci_dbg_ring_expansion and belongs to the event class xhci_log_msg, and adds tracepoints that trace the debug messages associated with the expansion of endpoint ring when there is not enough space allocated to hold all pending TRBs. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 3 ++- drivers/usb/host/xhci-ring.c | 4 ++-- drivers/usb/host/xhci-trace.h | 5 +++++ 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index ef27470b99e3..b150360d1e78 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -348,7 +348,8 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, return -ENOMEM; xhci_link_rings(xhci, ring, first, last, num_segs); - xhci_dbg(xhci, "ring expansion succeed, now has %d segments\n", + xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion, + "ring expansion succeed, now has %d segments", ring->num_segs); return 0; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f908e205f7d5..7b35af167e55 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2904,8 +2904,8 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, return -ENOMEM; } - xhci_dbg(xhci, "ERROR no room on ep ring, " - "try ring expansion\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion, + "ERROR no room on ep ring, try ring expansion"); num_trbs_needed = num_trbs - ep_ring->num_trbs_free; if (xhci_ring_expansion(xhci, ep_ring, num_trbs_needed, mem_flags)) { diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 11025fe6ebba..20364cc8d2fb 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -62,6 +62,11 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_init, TP_ARGS(vaf) ); +DEFINE_EVENT(xhci_log_msg, xhci_dbg_ring_expansion, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + DECLARE_EVENT_CLASS(xhci_log_ctx, TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int ep_num), -- cgit v1.2.3-58-ga151 From c10cf1189d7659ffd7dbe488a4cee7ec0dcdd9c6 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Wed, 14 Aug 2013 05:55:19 +0300 Subject: xhci: fix dma mask setup in xhci.c The function dma_set_mask() tests internally whether the dma_mask pointer for the device is initialized and fails if the dma_mask pointer is NULL. On pci platforms, the device dma_mask pointer is initialized, when pci devices are enumerated, to point to the pci_dev->dma_mask which is 0xffffffff. However, for non-pci platforms, the dma_mask pointer may not be initialized and in that case dma_set_mask() will fail. This patch initializes the dma_mask and the coherent_dma_mask to 32bits in xhci_plat_probe(), before the call to usb_create_hcd() that sets the "uses_dma" flag for the usb bus and the call to usb_add_hcd() that creates coherent dma pools for the usb hcd. Moreover, a call to dma_set_mask() does not set the device coherent_dma_mask. Since the xhci-hcd driver calls dma_alloc_coherent() and dma_pool_alloc() to allocate consistent DMA memory blocks, the coherent DMA address mask has to be set explicitly. This patch sets the coherent_dma_mask to 64bits in xhci_gen_setup() when the xHC is capable for 64-bit DMA addressing. If dma_set_mask() succeeds, for a given bitmask, it is guaranteed that the given bitmask is also supported for consistent DMA mappings. Other changes introduced in this patch are: - The return value of dma_set_mask() is checked to ensure that the required dma bitmask conforms with the host system's addressing capabilities. - The dma_mask setup code for the non-primary hcd was removed since both primary and non-primary hcd refer to the same generic device whose dma_mask and coherent_dma_mask are already set during the setup of the primary hcd. - The code for reading the HCCPARAMS register to find out the addressing capabilities of xHC was removed since its value is already cached in xhci->hccparams. - hcd->self.controller was replaced with the dev variable since it is already available. Signed-off-by: Xenia Ragiadakou Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-plat.c | 10 ++++++++++ drivers/usb/host/xhci.c | 19 +++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 3fcdaea21e24..be5e70d2300c 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "xhci.h" @@ -105,6 +106,15 @@ static int xhci_plat_probe(struct platform_device *pdev) if (!res) return -ENODEV; + /* Initialize dma_mask and coherent_dma_mask to 32-bits */ + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + else + dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) return -ENOMEM; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index eeb94d109059..e43b3889ab7a 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4825,7 +4825,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) struct xhci_hcd *xhci; struct device *dev = hcd->self.controller; int retval; - u32 temp; /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; @@ -4853,14 +4852,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* xHCI private pointer was set in xhci_pci_probe for the second * registered roothub. */ - xhci = hcd_to_xhci(hcd); - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_64BIT_ADDR(temp)) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); - } else { - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); - } return 0; } @@ -4899,12 +4890,12 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) goto error; xhci_dbg(xhci, "Reset complete\n"); - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_64BIT_ADDR(temp)) { + /* Set dma_mask and coherent_dma_mask to 64-bits, + * if xHC supports 64-bit addressing */ + if (HCC_64BIT_ADDR(xhci->hcc_params) && + !dma_set_mask(dev, DMA_BIT_MASK(64))) { xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); - } else { - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); } xhci_dbg(xhci, "Calling HCD init\n"); -- cgit v1.2.3-58-ga151 From 73b583af597542329e6adae44524da6f27afed62 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Wed, 14 Aug 2013 20:09:09 +0200 Subject: pl2303: simplify the else-if contruct for type_1 chips in pl2303_startup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need for two else-if constructs for the type_1 chip detection in pl2303_startup(), so merge them. Signed-off-by: Frank Schäfer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 6638c5d9079d..7efb39cd6f74 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -193,9 +193,8 @@ static int pl2303_startup(struct usb_serial *serial) type = type_0; else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) type = HX; - else if (serial->dev->descriptor.bDeviceClass == 0x00) - type = type_1; - else if (serial->dev->descriptor.bDeviceClass == 0xFF) + else if (serial->dev->descriptor.bDeviceClass == 0x00 + || serial->dev->descriptor.bDeviceClass == 0xFF) type = type_1; dev_dbg(&serial->interface->dev, "device type: %d\n", type); -- cgit v1.2.3-58-ga151 From a77a8c23e4db9fb1f776147eda0d85117359c700 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Wed, 14 Aug 2013 20:09:10 +0200 Subject: pl2303: improve the chip type information output on startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chip type distinction is getting more and more relevant and complicating, so always print the chip type. Printing a name string is also much better than just printing an internal index number. Signed-off-by: Frank Schäfer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 7efb39cd6f74..409000a836dd 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -177,6 +177,7 @@ static int pl2303_startup(struct usb_serial *serial) { struct pl2303_serial_private *spriv; enum pl2303_type type = type_0; + char *type_str = "unknown (treating as type_0)"; unsigned char *buf; spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); @@ -189,14 +190,18 @@ static int pl2303_startup(struct usb_serial *serial) return -ENOMEM; } - if (serial->dev->descriptor.bDeviceClass == 0x02) + if (serial->dev->descriptor.bDeviceClass == 0x02) { type = type_0; - else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) + type_str = "type_0"; + } else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) { type = HX; - else if (serial->dev->descriptor.bDeviceClass == 0x00 - || serial->dev->descriptor.bDeviceClass == 0xFF) + type_str = "X/HX"; + } else if (serial->dev->descriptor.bDeviceClass == 0x00 + || serial->dev->descriptor.bDeviceClass == 0xFF) { type = type_1; - dev_dbg(&serial->interface->dev, "device type: %d\n", type); + type_str = "type_1"; + } + dev_dbg(&serial->interface->dev, "device type: %s\n", type_str); spriv->type = type; usb_set_serial_data(serial, spriv); -- cgit v1.2.3-58-ga151 From 034d1527adebd302115c87ef343497a889638275 Mon Sep 17 00:00:00 2001 From: Frank Schäfer Date: Wed, 14 Aug 2013 20:09:11 +0200 Subject: pl2303: improve the chip type detection/distinction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver currently knows about 3 different PL2303 chip types: The two legacy chip types type_0 and type_1 (PL2303H ?) and the HX type. The device distinction is currently completely based on the examination of the USB descriptors. During the last years, Prolific has introduced further PL2303 chips, such as the HXD (HX rev. D), TA (which replaced the X/HX chips), SA, RA, EA and TB variants. Unfortunately, all these new chips are currently detected as HX chips, because they are all using the same bMaxPacketSize0 = 0x40 value in the USB device descriptor. At this point it is not clear if these chips are really working with the driver, there are just some positive indicators (like device manufacturers claiming Linux support for these devices or commit 8d48fdf689 "correctly handle baudrates above 115200" which should only be necessary for newer devices, ...) For a complete support of all devices, we need to distinguish between them, because they differ in several functional aspects, such as the maximum supported baud rate (HXD, TB, EA: 12Mbps, HX, TA: 6Mbps, RA: 1Mbps, SA: 115.2kbps), handshaking line support, RS422/485 and GPIO ports support (currently not supported by the driver). And there might be further differences that we don't know yet. This patch improves the chip type detection by evaluating the bcdDevice value of the device descriptor. The values are taken from the datasheets and are safe to use because manufacturers can't change them: 3.00: X/HX, TA 4.00: HXD, EA, RA, SA 5.00: TB The rest of the device descriptors is completely identical, so no further distinction is possible this way. Anyway, Prolifics "checkChipVersion.exe"-tool is definitely able to distinguish for example between the X/HX and the TA chips, so there must be a possibility to improve the distinction further... Signed-off-by: Frank Schäfer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 95 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 23 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 409000a836dd..e7a84f0f5179 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -134,10 +134,17 @@ MODULE_DEVICE_TABLE(usb, id_table); enum pl2303_type { - type_0, /* don't know the difference between type 0 and */ - type_1, /* type 1, until someone from prolific tells us... */ - HX, /* HX version of the pl2303 chip */ + type_0, /* H version ? */ + type_1, /* H version ? */ + HX_TA, /* HX(A) / X(A) / TA version */ /* TODO: improve */ + HXD_EA_RA_SA, /* HXD / EA / RA / SA version */ /* TODO: improve */ + TB, /* TB version */ }; +/* + * NOTE: don't know the difference between type 0 and type 1, + * until someone from Prolific tells us... + * TODO: distinguish between X/HX, TA and HXD, EA, RA, SA variants + */ struct pl2303_serial_private { enum pl2303_type type; @@ -194,8 +201,28 @@ static int pl2303_startup(struct usb_serial *serial) type = type_0; type_str = "type_0"; } else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) { - type = HX; - type_str = "X/HX"; + /* + * NOTE: The bcdDevice version is the only difference between + * the device descriptors of the X/HX, HXD, EA, RA, SA, TA, TB + */ + if (le16_to_cpu(serial->dev->descriptor.bcdDevice) == 0x300) { + type = HX_TA; + type_str = "X/HX/TA"; + } else if (le16_to_cpu(serial->dev->descriptor.bcdDevice) + == 0x400) { + type = HXD_EA_RA_SA; + type_str = "HXD/EA/RA/SA"; + } else if (le16_to_cpu(serial->dev->descriptor.bcdDevice) + == 0x500) { + type = TB; + type_str = "TB"; + } else { + dev_info(&serial->interface->dev, + "unknown/unsupported device type\n"); + kfree(spriv); + kfree(buf); + return -ENODEV; + } } else if (serial->dev->descriptor.bDeviceClass == 0x00 || serial->dev->descriptor.bDeviceClass == 0xFF) { type = type_1; @@ -216,10 +243,10 @@ static int pl2303_startup(struct usb_serial *serial) pl2303_vendor_read(0x8383, 0, serial, buf); pl2303_vendor_write(0, 1, serial); pl2303_vendor_write(1, 0, serial); - if (type == HX) - pl2303_vendor_write(2, 0x44, serial); - else + if (type == type_0 || type == type_1) pl2303_vendor_write(2, 0x24, serial); + else + pl2303_vendor_write(2, 0x44, serial); kfree(buf); return 0; @@ -284,12 +311,19 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type, const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800, 614400, 921600, - 1228800, 2457600, 3000000, 6000000 }; + 1228800, 2457600, 3000000, 6000000, 12000000 }; /* - * NOTE: The PL2303HX (tested with rev. 3A) also supports the following - * baud rates: 128000, 134400, 161280, 201600, 268800, 403200, 806400. - * As long as we are not using this encoding method for them, there is - * no point in complicating the code to support them. + * NOTE: With the exception of type_0/1 devices, the following + * additional baud rates are supported (tested with HX rev. 3A only): + * 110*, 56000*, 128000, 134400, 161280, 201600, 256000*, 268800, + * 403200, 806400. (*: not HX) + * + * Maximum values: HXD, TB: 12000000; HX, TA: 6000000; + * type_0+1: 1228800; RA: 921600; SA: 115200 + * + * As long as we are not using this encoding method for anything else + * than the type_0+1 and HX chips, there is no point in complicating + * the code to support them. */ int i; @@ -304,8 +338,14 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type, baud = baud_sup[i - 1]; else baud = baud_sup[i]; - /* type_0, type_1 only support up to 1228800 baud */ - if (type != HX) + /* Respect the chip type specific baud rate limits */ + /* + * FIXME: as long as we don't know how to distinguish between the + * HXD, EA, RA, and SA chip variants, allow the max. value of 12M. + */ + if (type == HX_TA) + baud = min_t(int, baud, 6000000); + else if (type == type_0 || type == type_1) baud = min_t(int, baud, 1228800); /* Direct (standard) baud rate encoding method */ put_unaligned_le32(baud, buf); @@ -344,10 +384,19 @@ static int pl2303_baudrate_encode_divisor(int baud, enum pl2303_type type, * Baud rates smaller than the specified 75 baud are definitely working * fine. */ - if (type == HX) - baud = min_t(int, baud, 6000000 * 1.1); - else + if (type == type_0 || type == type_1) baud = min_t(int, baud, 1228800 * 1.1); + else if (type == HX_TA) + baud = min_t(int, baud, 6000000 * 1.1); + else if (type == HXD_EA_RA_SA) + /* HXD, EA: 12Mbps; RA: 1Mbps; SA: 115200 bps */ + /* + * FIXME: as long as we don't know how to distinguish between + * these chip variants, allow the max. of these values + */ + baud = min_t(int, baud, 12000000 * 1.1); + else if (type == TB) + baud = min_t(int, baud, 12000000 * 1.1); /* Determine factors A and B */ A = 0; B = 12000000 * 32 / baud; /* 12MHz */ @@ -411,7 +460,7 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, * the device likely uses the same baud rate generator for both methods * so that there is likley no difference. */ - if (type != HX) + if (type == type_0 || type == type_1) baud = pl2303_baudrate_encode_direct(baud, type, buf); else baud = pl2303_baudrate_encode_divisor(baud, type, buf); @@ -549,10 +598,10 @@ static void pl2303_set_termios(struct tty_struct *tty, dev_dbg(&port->dev, "0xa1:0x21:0:0 %d - %7ph\n", i, buf); if (C_CRTSCTS(tty)) { - if (spriv->type == HX) - pl2303_vendor_write(0x0, 0x61, serial); - else + if (spriv->type == type_0 || spriv->type == type_1) pl2303_vendor_write(0x0, 0x41, serial); + else + pl2303_vendor_write(0x0, 0x61, serial); } else { pl2303_vendor_write(0x0, 0x0, serial); } @@ -589,7 +638,7 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) struct pl2303_serial_private *spriv = usb_get_serial_data(serial); int result; - if (spriv->type != HX) { + if (spriv->type == type_0 || spriv->type == type_1) { usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); } else { -- cgit v1.2.3-58-ga151 From 726a85caa30201eaadb993397a3e8ec23949c608 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Tue, 13 Aug 2013 13:40:13 -0700 Subject: usb: host: add Kconfig option for EHSET commit 9841f37a1c ("usb: ehci: Add support for SINGLE_STEP_SET_FEATURE test of EHSET") added additional code to the EHCI hub driver but it is anticipated to only have a limited audience (e.g. embedded silicon vendors and integrators). Avoid subjecting all EHCI (and in the future maybe xHCI/OHCI, etc.) HCD users to code bloat by conditionally compiling the EHSET-specific additions with a new Kconfig option, CONFIG_USB_HCD_TEST_MODE. Signed-off-by: Jack Pham Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 17 +++++++++++++++++ drivers/usb/host/ehci-hub.c | 8 +++++++- drivers/usb/host/ehci-q.c | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index e3ea970fe424..c41005a47802 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -699,3 +699,20 @@ config USB_HCD_SSB for ehci and ohci. If unsure, say N. + +config USB_HCD_TEST_MODE + bool "HCD test mode support" + ---help--- + Say 'Y' to enable additional software test modes that may be + supported by the host controller drivers. + + One such test mode is the Embedded High-speed Host Electrical Test + (EHSET) for EHCI host controller hardware, specifically the "Single + Step Set Feature" test. Typically this will be enabled for On-the-Go + or embedded hosts that need to undergo USB-IF compliance testing with + the aid of special testing hardware. In the future, this may expand + to include other tests that require support from a HCD driver. + + This option is of interest only to developers who need to validate + their USB hardware designs. It is not needed for normal use. If + unsure, say N. diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index f8b215fa0d93..3bf9f482710c 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -712,6 +712,8 @@ ehci_hub_descriptor ( } /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_HCD_TEST_MODE + #define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06 static void usb_ehset_completion(struct urb *urb) @@ -847,6 +849,7 @@ cleanup: kfree(buf); return retval; } +#endif /* CONFIG_USB_HCD_TEST_MODE */ /*-------------------------------------------------------------------------*/ static int ehci_hub_control ( @@ -1222,13 +1225,16 @@ static int ehci_hub_control ( * about the EHCI-specific stuff. */ case USB_PORT_FEAT_TEST: +#ifdef CONFIG_USB_HCD_TEST_MODE if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) { spin_unlock_irqrestore(&ehci->lock, flags); retval = ehset_single_step_set_feature(hcd, wIndex); spin_lock_irqsave(&ehci->lock, flags); break; - } else if (!selector || selector > 5) + } +#endif + if (!selector || selector > 5) goto error; spin_unlock_irqrestore(&ehci->lock, flags); ehci_quiesce(ehci); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 903d00dd6683..33336874c47b 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1139,6 +1139,7 @@ submit_async ( } /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_HCD_TEST_MODE /* * This function creates the qtds and submits them for the * SINGLE_STEP_SET_FEATURE Test. @@ -1238,6 +1239,7 @@ cleanup: qtd_list_free(ehci, urb, head); return -1; } +#endif /* CONFIG_USB_HCD_TEST_MODE */ /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-58-ga151 From e57e780b346a7277fdd3bde765d9d8728b99bfa1 Mon Sep 17 00:00:00 2001 From: "Sean O. Stalley" Date: Tue, 13 Aug 2013 10:36:12 -0700 Subject: usb: rh_call_control tbuf overflow fix rh_call_control() contains a buffer, tbuf, which it uses to hold USB descriptors. These discriptors are eventually copied into the transfer_buffer in the URB. The buffer in the URB is dynamically defined and is always large enough to hold the amount of data it requests. tbuf is currently statically allocated on the stack with a size of 15 bytes, regardless of the size specified in the URB. This patch dynamically allocates tbuf, and ensures that tbuf is at least as big as the buffer in the URB. If an hcd attempts to write a descriptor containing more than 15 bytes ( such as the Standard BOS Descriptor for hubs, defined in the USB3.0 Spec, section 10.13.1 ) the write would overflow the buffer and corrupt the stack. This patch addresses this behavior. Acked-by: Alan Stern Signed-off-by: Sean O. Stalley Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 5aa1d133eaf9..19ad3d2f8a24 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -466,17 +466,13 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; u8 *ubuf = urb->transfer_buffer; - /* - * tbuf should be as big as the BOS descriptor and - * the USB hub descriptor. - */ - u8 tbuf[USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE] - __attribute__((aligned(4))); - const u8 *bufp = tbuf; unsigned len = 0; int status; u8 patch_wakeup = 0; u8 patch_protocol = 0; + u16 tbuf_size; + u8 *tbuf = NULL; + const u8 *bufp; might_sleep(); @@ -496,6 +492,18 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) if (wLength > urb->transfer_buffer_length) goto error; + /* + * tbuf should be at least as big as the + * USB hub descriptor. + */ + tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength); + tbuf = kzalloc(tbuf_size, GFP_KERNEL); + if (!tbuf) + return -ENOMEM; + + bufp = tbuf; + + urb->actual_length = 0; switch (typeReq) { @@ -693,6 +701,8 @@ error: bDeviceProtocol = USB_HUB_PR_HS_SINGLE_TT; } + kfree(tbuf); + /* any errors get returned through the urb completion */ spin_lock_irq(&hcd_root_hub_lock); usb_hcd_unlink_urb_from_ep(hcd, urb); -- cgit v1.2.3-58-ga151 From bb674907111272ca40926ca3d3bad4046fffed52 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 14 Aug 2013 13:21:23 -0500 Subject: usb: dwc3: core: cope with NULL pdata if pdata is a NULL pointer we could cause a kernel oops when probing the driver. Make sure to cope with systems which won't pass pdata to the driver. Tested-by: Paul Zimmerman Reported-by: Paul Zimmerman Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 3ff6f0ad01df..577af1b237f2 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -393,7 +393,7 @@ static int dwc3_probe(struct platform_device *pdev) dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); dwc->dr_mode = of_usb_get_dr_mode(node); - } else { + } else if (pdata) { dwc->maximum_speed = pdata->maximum_speed; dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); @@ -401,6 +401,9 @@ static int dwc3_probe(struct platform_device *pdev) dwc->needs_fifo_resize = pdata->tx_fifo_resize; dwc->dr_mode = pdata->dr_mode; + } else { + dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); } /* default to superspeed if no maximum_speed passed */ -- cgit v1.2.3-58-ga151 From 5bb3da0ec0c3e60c15656250120c110317746d23 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 14 Aug 2013 12:43:55 +0300 Subject: usb: chipidea: ci_hdrc_imx: remove unused variable 'res' 'res' is not used anywhere, so let's get rid of it. Signed-off-by: Fabio Estevam Acked-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 14362c00db3f..06bc77554ec6 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -96,7 +96,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) CI_HDRC_PULLUP_ON_VBUS | CI_HDRC_DISABLE_STREAMING, }; - struct resource *res; int ret; if (of_find_property(pdev->dev.of_node, "fsl,usbmisc", NULL) @@ -109,12 +108,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) return -ENOMEM; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Can't get device resources!\n"); - return -ENOENT; - } - data->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(data->clk)) { dev_err(&pdev->dev, -- cgit v1.2.3-58-ga151 From 03779f05be6dfc48de99763f6c845a003e1e40f3 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 14 Aug 2013 12:43:56 +0300 Subject: usb: chipidea: move hw_phymode_configure() into probe Currently hw_phymode_configure() is located inside hw_device_reset(), which is only called by chipidea udc driver. When operating in host mode, we also need to call hw_phymode_configure() in order to properly configure the PHY mode, so move this function into probe. After this change, USB Host1 port on mx53qsb board is functional. Signed-off-by: Fabio Estevam Tested-by: Arnaud Patard Reviewed-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index a5df24c578fc..a5b3774bb223 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -264,8 +264,6 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode) while (hw_read(ci, OP_USBCMD, USBCMD_RST)) udelay(10); /* not RTOS friendly */ - hw_phymode_configure(ci); - if (ci->platdata->notify_event) ci->platdata->notify_event(ci, CI_HDRC_CONTROLLER_RESET_EVENT); @@ -457,6 +455,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (!ci->platdata->phy_mode) ci->platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); + hw_phymode_configure(ci); + if (!ci->platdata->dr_mode) ci->platdata->dr_mode = of_usb_get_dr_mode(dev->of_node); -- cgit v1.2.3-58-ga151 From 9d84d551169ae24830f7f47a04f57736805dae92 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:43:57 +0300 Subject: usb: chipidea: imx: delete the dead code Remove an unused macro leftover from the old initialization code. Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 06bc77554ec6..a9a3bcf99dbf 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -24,9 +24,6 @@ #include "ci.h" #include "ci_hdrc_imx.h" -#define pdev_to_phy(pdev) \ - ((struct usb_phy *)platform_get_drvdata(pdev)) - struct ci_hdrc_imx_data { struct usb_phy *phy; struct platform_device *ci_pdev; -- cgit v1.2.3-58-ga151 From a0cfdc6bc73bc47b63b05b850cf66cf67f2487bf Mon Sep 17 00:00:00 2001 From: Lothar Waßmann Date: Wed, 14 Aug 2013 12:43:58 +0300 Subject: usb: chipidea: improve kconfig 2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch provides a cleaner solution to the problem described in commit 20a677fd ("usb: chipidea: improve kconfig"). The goal to be achieved is to force USB_CHIPIDEA=m if either USB_EHCI_HCD=m or USB_GADGET=m. If both are 'y' USB_CHIPIDEA may be selected to be 'm' or 'y'. The old patch had the drawback, that USB_CHIPIDEA could be chosen as 'y' though USB_EHCI_HCD or USB_GADGET (or both) were 'm' leading to a situation where USB_CHIPIDEA_HOST or USB_CHIPIDEA_UDC vanished from the config options producing a compilable but dysfunctional driver. Signed-off-by: Lothar Waßmann Reviewed-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/Kconfig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index d1bd8ef1f9c1..4a851e15e58c 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -1,6 +1,6 @@ config USB_CHIPIDEA tristate "ChipIdea Highspeed Dual Role Controller" - depends on USB || USB_GADGET + depends on (USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET) help Say Y here if your system has a dual role high speed USB controller based on ChipIdea silicon IP. Currently, only the @@ -12,15 +12,14 @@ if USB_CHIPIDEA config USB_CHIPIDEA_UDC bool "ChipIdea device controller" - depends on USB_GADGET=y || (USB_CHIPIDEA=m && USB_GADGET=m) + depends on USB_GADGET help Say Y here to enable device controller functionality of the ChipIdea driver. config USB_CHIPIDEA_HOST bool "ChipIdea host controller" - depends on USB=y - depends on USB_EHCI_HCD=y || (USB_CHIPIDEA=m && USB_EHCI_HCD=m) + depends on USB_EHCI_HCD select USB_EHCI_ROOT_HUB_TT help Say Y here to enable host controller functionality of the -- cgit v1.2.3-58-ga151 From 769d92c411c3479835d2428edfd167c41d80c5f0 Mon Sep 17 00:00:00 2001 From: Lothar Waßmann Date: Wed, 14 Aug 2013 12:43:59 +0300 Subject: usb: chipidea: ci_hdrc_imx: remove an unsolicited module_put() call from ci_hdrc_imx_remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prevents the USB PHY refcount to be decremented below zero upon unloading the ci-hdrc-imx module. Signed-off-by: Lothar Waßmann Acked-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index a9a3bcf99dbf..005ad5f11b69 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -208,10 +208,8 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) if (data->reg_vbus) regulator_disable(data->reg_vbus); - if (data->phy) { + if (data->phy) usb_phy_shutdown(data->phy); - module_put(data->phy->dev->driver->owner); - } clk_disable_unprepare(data->clk); -- cgit v1.2.3-58-ga151 From 3b1280ca4b1743de3be04e9dd5907ec79d72483d Mon Sep 17 00:00:00 2001 From: Lothar Waßmann Date: Wed, 14 Aug 2013 12:44:00 +0300 Subject: usb: chipidea: don't clobber return value of ci_role_start() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a role fails to start, propagate the error code up the call stack from probe. Signed-off-by: Lothar Waßmann Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index a5b3774bb223..2400b7f87f97 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -497,7 +497,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) ret = ci_role_start(ci, ci->role); if (ret) { dev_err(dev, "can't start %s role\n", ci_role(ci)->name); - ret = -ENODEV; goto rm_wq; } -- cgit v1.2.3-58-ga151 From e98b44e90b2e8c58c5cbd921b8509e1b7b4940d3 Mon Sep 17 00:00:00 2001 From: Lothar Waßmann Date: Wed, 14 Aug 2013 12:44:01 +0300 Subject: usb: chipidea: prevent endless loop registering platform_devices when probe fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 40dcd0e ("usb: chipidea: add PTW, PTS and STS handling") introduced the following code to the ci_hdrc_probe() function: + if (!dev->of_node && dev->parent) + dev->of_node = dev->parent->of_node; This inadvertently associates the ci_hdrc device with the ci_hdrc_imx driver (which created the ci_hdrc device in the first place). This results in ci_hdrc_imx_probe() being run for the ci_hdrc device if ci_hdrc_probe() fails for some reason. ci_hdrc_imx_probe() will happily create a new ci_hdrc platform_device whose probing will likewise fail and trigger a new invocation of ci_hdrc_imx_probe() ... ad nauseam. Signed-off-by: Lothar Waßmann Reviewed-and-tested-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 2400b7f87f97..075b419a3076 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -404,15 +404,13 @@ static int ci_hdrc_probe(struct platform_device *pdev) void __iomem *base; int ret; enum usb_dr_mode dr_mode; + struct device_node *of_node = dev->of_node ?: dev->parent->of_node; if (!dev->platform_data) { dev_err(dev, "platform data missing\n"); return -ENODEV; } - if (!dev->of_node && dev->parent) - dev->of_node = dev->parent->of_node; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) @@ -453,12 +451,12 @@ static int ci_hdrc_probe(struct platform_device *pdev) } if (!ci->platdata->phy_mode) - ci->platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); + ci->platdata->phy_mode = of_usb_get_phy_mode(of_node); hw_phymode_configure(ci); if (!ci->platdata->dr_mode) - ci->platdata->dr_mode = of_usb_get_dr_mode(dev->of_node); + ci->platdata->dr_mode = of_usb_get_dr_mode(of_node); if (ci->platdata->dr_mode == USB_DR_MODE_UNKNOWN) ci->platdata->dr_mode = USB_DR_MODE_OTG; -- cgit v1.2.3-58-ga151 From 6a6243b164a9ef51ef8e4f4540cd34b723f74e5c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 14 Aug 2013 12:44:02 +0300 Subject: usb: chipidea: remove previous MODULE_ALIAS After the rename to ci_hdrc we ended up with two MODULE_ALIAS entries, so remove the old one. Signed-off-by: Fabio Estevam Reviewed-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 075b419a3076..5bb3e8154f87 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -545,7 +545,6 @@ static struct platform_driver ci_hdrc_driver = { module_platform_driver(ci_hdrc_driver); MODULE_ALIAS("platform:ci_hdrc"); -MODULE_ALIAS("platform:ci13xxx"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("David Lopo "); MODULE_DESCRIPTION("ChipIdea HDRC Driver"); -- cgit v1.2.3-58-ga151 From 1542d9c35d8166c54e0616574954a0f48449f331 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:03 +0300 Subject: usb: chipidea: move vbus regulator operation to core The vbus regulator is a common element for USB vbus operation, So, move it from glue layer to core. Tested-by: Marek Vasut Signed-off-by: Michael Grzeschik Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 26 ++------------------------ drivers/usb/chipidea/core.c | 23 +++++++++++++++++++++++ include/linux/usb/chipidea.h | 1 + 3 files changed, 26 insertions(+), 24 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 005ad5f11b69..30fdc2fd8771 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -19,7 +19,6 @@ #include #include #include -#include #include "ci.h" #include "ci_hdrc_imx.h" @@ -28,7 +27,6 @@ struct ci_hdrc_imx_data { struct usb_phy *phy; struct platform_device *ci_pdev; struct clk *clk; - struct regulator *reg_vbus; }; static const struct usbmisc_ops *usbmisc_ops; @@ -131,20 +129,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) goto err_clk; } - /* we only support host now, so enable vbus here */ - data->reg_vbus = devm_regulator_get(&pdev->dev, "vbus"); - if (!IS_ERR(data->reg_vbus)) { - ret = regulator_enable(data->reg_vbus); - if (ret) { - dev_err(&pdev->dev, - "Failed to enable vbus regulator, err=%d\n", - ret); - goto err_clk; - } - } else { - data->reg_vbus = NULL; - } - pdata.phy = data->phy; if (!pdev->dev.dma_mask) @@ -157,7 +141,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); - goto err; + goto err_clk; } } @@ -169,7 +153,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Can't register ci_hdrc platform device, err=%d\n", ret); - goto err; + goto err_clk; } if (usbmisc_ops && usbmisc_ops->post) { @@ -190,9 +174,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) disable_device: ci_hdrc_remove_device(data->ci_pdev); -err: - if (data->reg_vbus) - regulator_disable(data->reg_vbus); err_clk: clk_disable_unprepare(data->clk); return ret; @@ -205,9 +186,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); ci_hdrc_remove_device(data->ci_pdev); - if (data->reg_vbus) - regulator_disable(data->reg_vbus); - if (data->phy) usb_phy_shutdown(data->phy); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 5bb3e8154f87..13dbd511f59a 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -65,6 +65,7 @@ #include #include #include +#include #include "ci.h" #include "udc.h" @@ -342,6 +343,24 @@ static irqreturn_t ci_irq(int irq, void *data) return ret; } +static int ci_get_platdata(struct device *dev, + struct ci_hdrc_platform_data *platdata) +{ + /* Get the vbus regulator */ + platdata->reg_vbus = devm_regulator_get(dev, "vbus"); + if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { + platdata->reg_vbus = NULL; /* no vbus regualator is needed */ + } else if (IS_ERR(platdata->reg_vbus)) { + dev_err(dev, "Getting regulator error: %ld\n", + PTR_ERR(platdata->reg_vbus)); + return PTR_ERR(platdata->reg_vbus); + } + + return 0; +} + static DEFINE_IDA(ci_ida); struct platform_device *ci_hdrc_add_device(struct device *dev, @@ -351,6 +370,10 @@ struct platform_device *ci_hdrc_add_device(struct device *dev, struct platform_device *pdev; int id, ret; + ret = ci_get_platdata(dev, platdata); + if (ret) + return ERR_PTR(ret); + id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 25629948c842..ce4e1aa071b3 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -24,6 +24,7 @@ struct ci_hdrc_platform_data { #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 void (*notify_event) (struct ci_hdrc *ci, unsigned event); + struct regulator *reg_vbus; }; /* Default offset of capability registers */ -- cgit v1.2.3-58-ga151 From 40ed51a4b8588055ccd8e5420c76290659c4c974 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:04 +0300 Subject: usb: chipidea: host: add vbus regulator control For boards which have board level vbus control (eg, through gpio), we need to vbus operation according to below rules: - For host, we need open vbus before start hcd, and close it after remove hcd. - For otg, the vbus needs to be on/off when usb role switches. When the host roles begins, it opens vbus; when the host role finishes, it closes vbus. We put vbus operation to host as host is the only vbus user, When we are at host mode, the vbus is on, when we are not at host mode, vbus should be off. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/host.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 9b3aaf132bc3..8cf8d28c88d6 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "../host/ehci.h" @@ -65,9 +66,19 @@ static int host_start(struct ci_hdrc *ci) ehci->has_hostpc = ci->hw_bank.lpm; ehci->has_tdi_phy_lpm = ci->hw_bank.lpm; + if (ci->platdata->reg_vbus) { + ret = regulator_enable(ci->platdata->reg_vbus); + if (ret) { + dev_err(ci->dev, + "Failed to enable vbus regulator, ret=%d\n", + ret); + goto put_hcd; + } + } + ret = usb_add_hcd(hcd, 0, 0); if (ret) - usb_put_hcd(hcd); + goto disable_reg; else ci->hcd = hcd; @@ -75,6 +86,14 @@ static int host_start(struct ci_hdrc *ci) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); return ret; + +disable_reg: + regulator_disable(ci->platdata->reg_vbus); + +put_hcd: + usb_put_hcd(hcd); + + return ret; } static void host_stop(struct ci_hdrc *ci) @@ -83,6 +102,8 @@ static void host_stop(struct ci_hdrc *ci) usb_remove_hcd(hcd); usb_put_hcd(hcd); + if (ci->platdata->reg_vbus) + regulator_disable(ci->platdata->reg_vbus); } int ci_hdrc_host_init(struct ci_hdrc *ci) -- cgit v1.2.3-58-ga151 From d66895f9df6b880fa3a280f5bdfe919169ca37dd Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:05 +0300 Subject: usb: chipidea: udc: otg_set_peripheral is useless for some chipidea users It is useless at below cases: - If we implement both usb host and device at chipidea driver. - If we don't need phy->otg. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index e475fcda1d68..116c76203884 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1805,7 +1805,12 @@ static int udc_start(struct ci_hdrc *ci) if (ci->transceiver) { retval = otg_set_peripheral(ci->transceiver->otg, &ci->gadget); - if (retval) + /* + * If we implement all USB functions using chipidea drivers, + * it doesn't need to call above API, meanwhile, if we only + * use gadget function, calling above API is useless. + */ + if (retval && retval != -ENOTSUPP) goto put_transceiver; } -- cgit v1.2.3-58-ga151 From c10b4f033e7c0ddba2b7b699d01109e4de46e831 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:06 +0300 Subject: usb: chipidea: otg: add otg file used to access otgsc This file is mainly used to access otgsc currently, it may add otg related things in the future. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/Makefile | 2 +- drivers/usb/chipidea/bits.h | 10 ++++++++++ drivers/usb/chipidea/core.c | 3 ++- drivers/usb/chipidea/otg.c | 35 +++++++++++++++++++++++++++++++++++ drivers/usb/chipidea/otg.h | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/chipidea/otg.c create mode 100644 drivers/usb/chipidea/otg.h (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 6cf5f68dedd8..a99d980454a6 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -2,7 +2,7 @@ ccflags-$(CONFIG_USB_CHIPIDEA_DEBUG) := -DDEBUG obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o -ci_hdrc-y := core.o +ci_hdrc-y := core.o otg.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index 1b23e354f9fb..464584c6ccae 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h @@ -79,11 +79,21 @@ #define OTGSC_ASVIS BIT(18) #define OTGSC_BSVIS BIT(19) #define OTGSC_BSEIS BIT(20) +#define OTGSC_1MSIS BIT(21) +#define OTGSC_DPIS BIT(22) #define OTGSC_IDIE BIT(24) #define OTGSC_AVVIE BIT(25) #define OTGSC_ASVIE BIT(26) #define OTGSC_BSVIE BIT(27) #define OTGSC_BSEIE BIT(28) +#define OTGSC_1MSIE BIT(29) +#define OTGSC_DPIE BIT(30) +#define OTGSC_INT_EN_BITS (OTGSC_IDIE | OTGSC_AVVIE | OTGSC_ASVIE \ + | OTGSC_BSVIE | OTGSC_BSEIE | OTGSC_1MSIE \ + | OTGSC_DPIE) +#define OTGSC_INT_STATUS_BITS (OTGSC_IDIS | OTGSC_AVVIS | OTGSC_ASVIS \ + | OTGSC_BSVIS | OTGSC_BSEIS | OTGSC_1MSIS \ + | OTGSC_DPIS) /* USBMODE */ #define USBMODE_CM (0x03UL << 0) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 13dbd511f59a..2f70080023ae 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -72,6 +72,7 @@ #include "bits.h" #include "host.h" #include "debug.h" +#include "otg.h" /* Controller register map */ static uintptr_t ci_regs_nolpm[] = { @@ -528,7 +529,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) goto stop; if (ci->is_otg) - hw_write(ci, OP_OTGSC, OTGSC_IDIE, OTGSC_IDIE); + ci_hdrc_otg_init(ci); ret = dbg_create_files(ci); if (!ret) diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c new file mode 100644 index 000000000000..999a085491d7 --- /dev/null +++ b/drivers/usb/chipidea/otg.c @@ -0,0 +1,35 @@ +/* + * otg.c - ChipIdea USB IP core OTG driver + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * Author: Peter Chen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This file mainly handles otgsc register, it may include OTG operation + * in the future. + */ + +#include +#include +#include + +#include "ci.h" +#include "bits.h" +#include "otg.h" + +/** + * ci_hdrc_otg_init - initialize otgsc bits + * ci: the controller + */ +int ci_hdrc_otg_init(struct ci_hdrc *ci) +{ + ci_enable_otg_interrupt(ci, OTGSC_IDIE); + + return 0; +} diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h new file mode 100644 index 000000000000..376eaee73042 --- /dev/null +++ b/drivers/usb/chipidea/otg.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * Author: Peter Chen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVERS_USB_CHIPIDEA_OTG_H +#define __DRIVERS_USB_CHIPIDEA_OTG_H + +static inline void ci_clear_otg_interrupt(struct ci_hdrc *ci, u32 bits) +{ + /* Only clear request bits */ + hw_write(ci, OP_OTGSC, OTGSC_INT_STATUS_BITS, bits); +} + +static inline void ci_enable_otg_interrupt(struct ci_hdrc *ci, u32 bits) +{ + hw_write(ci, OP_OTGSC, bits, bits); +} + +static inline void ci_disable_otg_interrupt(struct ci_hdrc *ci, u32 bits) +{ + hw_write(ci, OP_OTGSC, bits, 0); +} + +int ci_hdrc_otg_init(struct ci_hdrc *ci); + +#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */ -- cgit v1.2.3-58-ga151 From 3f124d233e97db96d9471d1fb346335d43d8650d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:07 +0300 Subject: usb: chipidea: add role init and destroy APIs - The role's init will be called at probe procedure. - The role's destroy will be called at fail patch at probe and driver's removal. - The role's start/stop will be called when specific role has started. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 10 ++++++++-- drivers/usb/chipidea/host.c | 7 +++++++ drivers/usb/chipidea/host.h | 6 ++++++ drivers/usb/chipidea/udc.c | 36 +++++++++++++++++++++++++++--------- drivers/usb/chipidea/udc.h | 6 ++++++ 5 files changed, 54 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 2f70080023ae..75afc524b659 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -420,6 +420,12 @@ void ci_hdrc_remove_device(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); +static inline void ci_role_destroy(struct ci_hdrc *ci) +{ + ci_hdrc_gadget_destroy(ci); + ci_hdrc_host_destroy(ci); +} + static int ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -537,7 +543,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) free_irq(ci->irq, ci); stop: - ci_role_stop(ci); + ci_role_destroy(ci); rm_wq: flush_workqueue(ci->wq); destroy_workqueue(ci->wq); @@ -553,7 +559,7 @@ static int ci_hdrc_remove(struct platform_device *pdev) flush_workqueue(ci->wq); destroy_workqueue(ci->wq); free_irq(ci->irq, ci); - ci_role_stop(ci); + ci_role_destroy(ci); return 0; } diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 8cf8d28c88d6..6f96795dd20c 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -106,6 +106,13 @@ static void host_stop(struct ci_hdrc *ci) regulator_disable(ci->platdata->reg_vbus); } + +void ci_hdrc_host_destroy(struct ci_hdrc *ci) +{ + if (ci->role == CI_ROLE_HOST) + host_stop(ci); +} + int ci_hdrc_host_init(struct ci_hdrc *ci) { struct ci_role_driver *rdrv; diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h index 058875c15333..5707bf379bfb 100644 --- a/drivers/usb/chipidea/host.h +++ b/drivers/usb/chipidea/host.h @@ -4,6 +4,7 @@ #ifdef CONFIG_USB_CHIPIDEA_HOST int ci_hdrc_host_init(struct ci_hdrc *ci); +void ci_hdrc_host_destroy(struct ci_hdrc *ci); #else @@ -12,6 +13,11 @@ static inline int ci_hdrc_host_init(struct ci_hdrc *ci) return -ENXIO; } +static inline void ci_hdrc_host_destroy(struct ci_hdrc *ci) +{ + +} + #endif #endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */ diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 116c76203884..24a100d751ca 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -27,6 +27,7 @@ #include "udc.h" #include "bits.h" #include "debug.h" +#include "otg.h" /* control endpoint description */ static const struct usb_endpoint_descriptor @@ -1844,13 +1845,13 @@ free_qh_pool: } /** - * udc_remove: parent remove must call this to remove UDC + * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC * * No interrupts active, the IRQ has been released */ -static void udc_stop(struct ci_hdrc *ci) +void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) { - if (ci == NULL) + if (!ci->roles[CI_ROLE_GADGET]) return; usb_del_gadget_udc(&ci->gadget); @@ -1865,15 +1866,32 @@ static void udc_stop(struct ci_hdrc *ci) if (ci->global_phy) usb_put_phy(ci->transceiver); } - /* my kobject is dynamic, I swear! */ - memset(&ci->gadget, 0, sizeof(ci->gadget)); +} + +static int udc_id_switch_for_device(struct ci_hdrc *ci) +{ + if (ci->is_otg) { + ci_clear_otg_interrupt(ci, OTGSC_BSVIS); + ci_enable_otg_interrupt(ci, OTGSC_BSVIE); + } + + return 0; +} + +static void udc_id_switch_for_host(struct ci_hdrc *ci) +{ + if (ci->is_otg) { + /* host doesn't care B_SESSION_VALID event */ + ci_clear_otg_interrupt(ci, OTGSC_BSVIS); + ci_disable_otg_interrupt(ci, OTGSC_BSVIE); + } } /** * ci_hdrc_gadget_init - initialize device related bits * ci: the controller * - * This function enables the gadget role, if the device is "device capable". + * This function initializes the gadget, if the device is "device capable". */ int ci_hdrc_gadget_init(struct ci_hdrc *ci) { @@ -1886,11 +1904,11 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) if (!rdrv) return -ENOMEM; - rdrv->start = udc_start; - rdrv->stop = udc_stop; + rdrv->start = udc_id_switch_for_device; + rdrv->stop = udc_id_switch_for_host; rdrv->irq = udc_irq; rdrv->name = "gadget"; ci->roles[CI_ROLE_GADGET] = rdrv; - return 0; + return udc_start(ci); } diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 455ac2169226..e66df0020bd4 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -84,6 +84,7 @@ struct ci_hw_req { #ifdef CONFIG_USB_CHIPIDEA_UDC int ci_hdrc_gadget_init(struct ci_hdrc *ci); +void ci_hdrc_gadget_destroy(struct ci_hdrc *ci); #else @@ -92,6 +93,11 @@ static inline int ci_hdrc_gadget_init(struct ci_hdrc *ci) return -ENXIO; } +static inline void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) +{ + +} + #endif #endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */ -- cgit v1.2.3-58-ga151 From 577b232fc9caba1b6f7a3bb9901c00b10e0ca1ba Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:08 +0300 Subject: usb: chipidea: add flag CI_HDRC_DUAL_ROLE_NOT_OTG Since we need otgsc to know vbus's status at some chipidea controllers even it is peripheral-only mode. Besides, some SoCs (eg, AR9331 SoC) don't have otgsc register even the DCCPARAMS_DC and DCCPARAMS_HC are both 1 at CAP_DCCPARAMS. We inroduce flag CI_HDRC_DUAL_ROLE_NOT_OTG to indicate if the controller is dual role, but not supports OTG. If this flag is not set, we follow the rule that if DCCPARAMS_DC and DCCPARAMS_HC are both 1 at CAP_DCCPARAMS, then this controller is otg capable. Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 37 ++++++++++++++++++++++++++++++------- include/linux/usb/chipidea.h | 5 +++++ 2 files changed, 35 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 75afc524b659..ab01e18bc2f8 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -426,6 +426,18 @@ static inline void ci_role_destroy(struct ci_hdrc *ci) ci_hdrc_host_destroy(ci); } +static void ci_get_otg_capable(struct ci_hdrc *ci) +{ + if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) + ci->is_otg = false; + else + ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, + DCCPARAMS_DC | DCCPARAMS_HC) + == (DCCPARAMS_DC | DCCPARAMS_HC)); + if (ci->is_otg) + dev_dbg(ci->dev, "It is OTG capable controller\n"); +} + static int ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -480,6 +492,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) return -ENODEV; } + ci_get_otg_capable(ci); + if (!ci->platdata->phy_mode) ci->platdata->phy_mode = of_usb_get_phy_mode(of_node); @@ -512,10 +526,22 @@ static int ci_hdrc_probe(struct platform_device *pdev) } if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { - ci->is_otg = true; - /* ID pin needs 1ms debouce time, we delay 2ms for safe */ - mdelay(2); - ci->role = ci_otg_role(ci); + if (ci->is_otg) { + /* + * ID pin needs 1ms debouce time, + * we delay 2ms for safe. + */ + mdelay(2); + ci->role = ci_otg_role(ci); + ci_hdrc_otg_init(ci); + } else { + /* + * If the controller is not OTG capable, but support + * role switch, the defalt role is gadget, and the + * user can switch it through debugfs. + */ + ci->role = CI_ROLE_GADGET; + } } else { ci->role = ci->roles[CI_ROLE_HOST] ? CI_ROLE_HOST @@ -534,9 +560,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ret) goto stop; - if (ci->is_otg) - ci_hdrc_otg_init(ci); - ret = dbg_create_files(ci); if (!ret) return 0; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index ce4e1aa071b3..10a607ce9090 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -20,6 +20,11 @@ struct ci_hdrc_platform_data { #define CI_HDRC_REQUIRE_TRANSCEIVER BIT(1) #define CI_HDRC_PULLUP_ON_VBUS BIT(2) #define CI_HDRC_DISABLE_STREAMING BIT(3) + /* + * Only set it when DCCPARAMS.DC==1 and DCCPARAMS.HC==1, + * but otg is not supported (no register otgsc). + */ +#define CI_HDRC_DUAL_ROLE_NOT_OTG BIT(4) enum usb_dr_mode dr_mode; #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 -- cgit v1.2.3-58-ga151 From c344b518008ada3170349d1c06e8a30224400b29 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:09 +0300 Subject: usb: chipidea: disable all interrupts and clear all interrupts status During the initialization, it needs to disable all interrupts enable bit as well as clear all interrupts status bits to avoid exceptional interrupt. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index ab01e18bc2f8..e9cfd3193d65 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -199,6 +199,12 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) if (ci->hw_ep_max > ENDPT_MAX) return -ENODEV; + /* Disable all interrupts bits */ + hw_write(ci, OP_USBINTR, 0xffffffff, 0); + + /* Clear all interrupts status bits*/ + hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); + dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n", ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); @@ -434,8 +440,11 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC | DCCPARAMS_HC) == (DCCPARAMS_DC | DCCPARAMS_HC)); - if (ci->is_otg) + if (ci->is_otg) { dev_dbg(ci->dev, "It is OTG capable controller\n"); + ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS); + ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS); + } } static int ci_hdrc_probe(struct platform_device *pdev) -- cgit v1.2.3-58-ga151 From cbec6bd55a45fa88218ec5ea5ae91f9b96d158d0 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:10 +0300 Subject: usb: chipidea: move otg related things to otg file Move otg related things to otg file. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 63 ++++++++++----------------------------------- drivers/usb/chipidea/otg.c | 57 ++++++++++++++++++++++++++++++++++++++-- drivers/usb/chipidea/otg.h | 2 ++ 3 files changed, 70 insertions(+), 52 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index e9cfd3193d65..ec6c984d2a6e 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -294,40 +294,6 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode) return 0; } -/** - * ci_otg_role - pick role based on ID pin state - * @ci: the controller - */ -static enum ci_role ci_otg_role(struct ci_hdrc *ci) -{ - u32 sts = hw_read(ci, OP_OTGSC, ~0); - enum ci_role role = sts & OTGSC_ID - ? CI_ROLE_GADGET - : CI_ROLE_HOST; - - return role; -} - -/** - * ci_role_work - perform role changing based on ID pin - * @work: work struct - */ -static void ci_role_work(struct work_struct *work) -{ - struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); - enum ci_role role = ci_otg_role(ci); - - if (role != ci->role) { - dev_dbg(ci->dev, "switching from %s to %s\n", - ci_role(ci)->name, ci->roles[role]->name); - - ci_role_stop(ci); - ci_role_start(ci, role); - } - - enable_irq(ci->irq); -} - static irqreturn_t ci_irq(int irq, void *data) { struct ci_hdrc *ci = data; @@ -430,6 +396,8 @@ static inline void ci_role_destroy(struct ci_hdrc *ci) { ci_hdrc_gadget_destroy(ci); ci_hdrc_host_destroy(ci); + if (ci->is_otg) + ci_hdrc_otg_destroy(ci); } static void ci_get_otg_capable(struct ci_hdrc *ci) @@ -494,13 +462,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) return -ENODEV; } - INIT_WORK(&ci->work, ci_role_work); - ci->wq = create_singlethread_workqueue("ci_otg"); - if (!ci->wq) { - dev_err(dev, "can't create workqueue\n"); - return -ENODEV; - } - ci_get_otg_capable(ci); if (!ci->platdata->phy_mode) @@ -530,8 +491,15 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { dev_err(dev, "no supported roles\n"); - ret = -ENODEV; - goto rm_wq; + return -ENODEV; + } + + if (ci->is_otg) { + ret = ci_hdrc_otg_init(ci); + if (ret) { + dev_err(dev, "init otg fails, ret = %d\n", ret); + goto stop; + } } if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { @@ -542,7 +510,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) */ mdelay(2); ci->role = ci_otg_role(ci); - ci_hdrc_otg_init(ci); + ci_enable_otg_interrupt(ci, OTGSC_IDIE); } else { /* * If the controller is not OTG capable, but support @@ -560,7 +528,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) ret = ci_role_start(ci, ci->role); if (ret) { dev_err(dev, "can't start %s role\n", ci_role(ci)->name); - goto rm_wq; + goto stop; } platform_set_drvdata(pdev, ci); @@ -576,9 +544,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) free_irq(ci->irq, ci); stop: ci_role_destroy(ci); -rm_wq: - flush_workqueue(ci->wq); - destroy_workqueue(ci->wq); return ret; } @@ -588,8 +553,6 @@ static int ci_hdrc_remove(struct platform_device *pdev) struct ci_hdrc *ci = platform_get_drvdata(pdev); dbg_remove_files(ci); - flush_workqueue(ci->wq); - destroy_workqueue(ci->wq); free_irq(ci->irq, ci); ci_role_destroy(ci); diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 999a085491d7..3b66cbe58d52 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -24,12 +24,65 @@ #include "otg.h" /** - * ci_hdrc_otg_init - initialize otgsc bits + * ci_otg_role - pick role based on ID pin state + * @ci: the controller + */ +enum ci_role ci_otg_role(struct ci_hdrc *ci) +{ + u32 sts = hw_read(ci, OP_OTGSC, ~0); + enum ci_role role = sts & OTGSC_ID + ? CI_ROLE_GADGET + : CI_ROLE_HOST; + + return role; +} + +/** + * ci_role_work - perform role changing based on ID pin + * @work: work struct + */ +static void ci_role_work(struct work_struct *work) +{ + struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); + enum ci_role role = ci_otg_role(ci); + + if (role != ci->role) { + dev_dbg(ci->dev, "switching from %s to %s\n", + ci_role(ci)->name, ci->roles[role]->name); + + ci_role_stop(ci); + ci_role_start(ci, role); + } + + enable_irq(ci->irq); +} + +/** + * ci_hdrc_otg_init - initialize otg struct * ci: the controller */ int ci_hdrc_otg_init(struct ci_hdrc *ci) { - ci_enable_otg_interrupt(ci, OTGSC_IDIE); + INIT_WORK(&ci->work, ci_role_work); + ci->wq = create_singlethread_workqueue("ci_otg"); + if (!ci->wq) { + dev_err(ci->dev, "can't create workqueue\n"); + return -ENODEV; + } return 0; } + +/** + * ci_hdrc_otg_destroy - destroy otg struct + * ci: the controller + */ +void ci_hdrc_otg_destroy(struct ci_hdrc *ci) +{ + if (ci->wq) { + flush_workqueue(ci->wq); + destroy_workqueue(ci->wq); + } + ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS); + ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS); +} diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h index 376eaee73042..8acf3df4e3e5 100644 --- a/drivers/usb/chipidea/otg.h +++ b/drivers/usb/chipidea/otg.h @@ -28,5 +28,7 @@ static inline void ci_disable_otg_interrupt(struct ci_hdrc *ci, u32 bits) } int ci_hdrc_otg_init(struct ci_hdrc *ci); +void ci_hdrc_otg_destroy(struct ci_hdrc *ci); +enum ci_role ci_otg_role(struct ci_hdrc *ci); #endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */ -- cgit v1.2.3-58-ga151 From a107f8c505cd8606ae192d24c70b380e980fbe67 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:11 +0300 Subject: usb: chipidea: add vbus interrupt handler We add vbus interrupt handler at ci_otg_work, it uses OTGSC_BSV(at otgsc) to know it is connect or disconnet event. Meanwhile, we introduce two flags id_event and b_sess_valid_event to indicate it is an id interrupt or a vbus interrupt. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 5 +++++ drivers/usb/chipidea/core.c | 28 +++++++++++++++++++++++----- drivers/usb/chipidea/otg.c | 42 +++++++++++++++++++++++++++++++++++------- drivers/usb/chipidea/otg.h | 1 + drivers/usb/chipidea/udc.c | 7 +++++++ 5 files changed, 71 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 33cb29f36e06..316036392e11 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -132,6 +132,9 @@ struct hw_bank { * @transceiver: pointer to USB PHY, if any * @hcd: pointer to usb_hcd for ehci host driver * @debugfs: root dentry for this controller in debugfs + * @id_event: indicates there is an id event, and handled at ci_otg_work + * @b_sess_valid_event: indicates there is a vbus event, and handled + * at ci_otg_work */ struct ci_hdrc { struct device *dev; @@ -168,6 +171,8 @@ struct ci_hdrc { struct usb_phy *transceiver; struct usb_hcd *hcd; struct dentry *debugfs; + bool id_event; + bool b_sess_valid_event; }; static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index ec6c984d2a6e..c95e098fe9db 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -303,16 +303,34 @@ static irqreturn_t ci_irq(int irq, void *data) if (ci->is_otg) otgsc = hw_read(ci, OP_OTGSC, ~0); - if (ci->role != CI_ROLE_END) - ret = ci_role(ci)->irq(ci); + /* + * Handle id change interrupt, it indicates device/host function + * switch. + */ + if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { + ci->id_event = true; + ci_clear_otg_interrupt(ci, OTGSC_IDIS); + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + return IRQ_HANDLED; + } - if (ci->is_otg && (otgsc & OTGSC_IDIS)) { - hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS); + /* + * Handle vbus change interrupt, it indicates device connection + * and disconnection events. + */ + if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { + ci->b_sess_valid_event = true; + ci_clear_otg_interrupt(ci, OTGSC_BSVIS); disable_irq_nosync(ci->irq); queue_work(ci->wq, &ci->work); - ret = IRQ_HANDLED; + return IRQ_HANDLED; } + /* Handle device/host interrupt */ + if (ci->role != CI_ROLE_END) + ret = ci_role(ci)->irq(ci); + return ret; } diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 3b66cbe58d52..7f37484ca362 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -37,13 +37,23 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci) return role; } -/** - * ci_role_work - perform role changing based on ID pin - * @work: work struct - */ -static void ci_role_work(struct work_struct *work) +void ci_handle_vbus_change(struct ci_hdrc *ci) +{ + u32 otgsc; + + if (!ci->is_otg) + return; + + otgsc = hw_read(ci, OP_OTGSC, ~0); + + if (otgsc & OTGSC_BSV) + usb_gadget_vbus_connect(&ci->gadget); + else + usb_gadget_vbus_disconnect(&ci->gadget); +} + +static void ci_handle_id_switch(struct ci_hdrc *ci) { - struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); enum ci_role role = ci_otg_role(ci); if (role != ci->role) { @@ -53,17 +63,35 @@ static void ci_role_work(struct work_struct *work) ci_role_stop(ci); ci_role_start(ci, role); } +} +/** + * ci_otg_work - perform otg (vbus/id) event handle + * @work: work struct + */ +static void ci_otg_work(struct work_struct *work) +{ + struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); + + if (ci->id_event) { + ci->id_event = false; + ci_handle_id_switch(ci); + } else if (ci->b_sess_valid_event) { + ci->b_sess_valid_event = false; + ci_handle_vbus_change(ci); + } else + dev_err(ci->dev, "unexpected event occurs at %s\n", __func__); enable_irq(ci->irq); } + /** * ci_hdrc_otg_init - initialize otg struct * ci: the controller */ int ci_hdrc_otg_init(struct ci_hdrc *ci) { - INIT_WORK(&ci->work, ci_role_work); + INIT_WORK(&ci->work, ci_otg_work); ci->wq = create_singlethread_workqueue("ci_otg"); if (!ci->wq) { dev_err(ci->dev, "can't create workqueue\n"); diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h index 8acf3df4e3e5..2d9f090733bc 100644 --- a/drivers/usb/chipidea/otg.h +++ b/drivers/usb/chipidea/otg.h @@ -30,5 +30,6 @@ static inline void ci_disable_otg_interrupt(struct ci_hdrc *ci, u32 bits) int ci_hdrc_otg_init(struct ci_hdrc *ci); void ci_hdrc_otg_destroy(struct ci_hdrc *ci); enum ci_role ci_otg_role(struct ci_hdrc *ci); +void ci_handle_vbus_change(struct ci_hdrc *ci); #endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */ diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 24a100d751ca..c70ce3891d35 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -85,8 +85,10 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma) /* interrupt, error, port change, reset, sleep/suspend */ hw_write(ci, OP_USBINTR, ~0, USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); + hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); } else { hw_write(ci, OP_USBINTR, ~0, 0); + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); } return 0; } @@ -1460,6 +1462,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) pm_runtime_get_sync(&_gadget->dev); hw_device_reset(ci, USBMODE_CM_DC); hw_device_state(ci, ci->ep0out->qh.dma); + dev_dbg(ci->dev, "Connected to host\n"); } else { hw_device_state(ci, 0); if (ci->platdata->notify_event) @@ -1467,6 +1470,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) CI_HDRC_CONTROLLER_STOPPED_EVENT); _gadget_stop_activity(&ci->gadget); pm_runtime_put_sync(&_gadget->dev); + dev_dbg(ci->dev, "Disconnected from host\n"); } } @@ -1822,6 +1826,9 @@ static int udc_start(struct ci_hdrc *ci) pm_runtime_no_callbacks(&ci->gadget.dev); pm_runtime_enable(&ci->gadget.dev); + /* Update ci->vbus_active */ + ci_handle_vbus_change(ci); + return retval; remove_trans: -- cgit v1.2.3-58-ga151 From 22fa84455a2a80566ead8efe4b5e629f2375da60 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:12 +0300 Subject: usb: chipidea: add wait vbus lower than OTGSC_BSV before role starts When the gadget role starts, we need to make sure the vbus is lower than OTGSC_BSV, or there will be an vbus interrupt since we use B_SESSION_VALID as vbus interrupt to indicate connect and disconnect. When the host role starts, it may not be useful to wait vbus to lower than OTGSC_BSV, but it can indicate some hardware problems like the vbus is still higher than OTGSC_BSV after we disconnect to host some time later (5000 milliseconds currently), which is obvious not correct. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 3 +++ drivers/usb/chipidea/core.c | 32 ++++++++++++++++++++++++++++++++ drivers/usb/chipidea/otg.c | 4 ++++ 3 files changed, 39 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 316036392e11..1c94fc5257f4 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -308,4 +308,7 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode); u8 hw_port_test_get(struct ci_hdrc *ci); +int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask, + u32 value, unsigned int timeout_ms); + #endif /* __DRIVERS_USB_CHIPIDEA_CI_H */ diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index c95e098fe9db..94626409559a 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -294,6 +294,38 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode) return 0; } +/** + * hw_wait_reg: wait the register value + * + * Sometimes, it needs to wait register value before going on. + * Eg, when switch to device mode, the vbus value should be lower + * than OTGSC_BSV before connects to host. + * + * @ci: the controller + * @reg: register index + * @mask: mast bit + * @value: the bit value to wait + * @timeout_ms: timeout in millisecond + * + * This function returns an error code if timeout + */ +int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask, + u32 value, unsigned int timeout_ms) +{ + unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms); + + while (hw_read(ci, reg, mask) != value) { + if (time_after(jiffies, elapse)) { + dev_err(ci->dev, "timeout waiting for %08x in %d\n", + mask, reg); + return -ETIMEDOUT; + } + msleep(20); + } + + return 0; +} + static irqreturn_t ci_irq(int irq, void *data) { struct ci_hdrc *ci = data; diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 7f37484ca362..39bd7ec8bf75 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -52,6 +52,7 @@ void ci_handle_vbus_change(struct ci_hdrc *ci) usb_gadget_vbus_disconnect(&ci->gadget); } +#define CI_VBUS_STABLE_TIMEOUT_MS 5000 static void ci_handle_id_switch(struct ci_hdrc *ci) { enum ci_role role = ci_otg_role(ci); @@ -61,6 +62,9 @@ static void ci_handle_id_switch(struct ci_hdrc *ci) ci_role(ci)->name, ci->roles[role]->name); ci_role_stop(ci); + /* wait vbus lower than OTGSC_BSV */ + hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0, + CI_VBUS_STABLE_TIMEOUT_MS); ci_role_start(ci, role); } } -- cgit v1.2.3-58-ga151 From e7e621eb84b0708f6a04ccb453d47e77d22440e4 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:13 +0300 Subject: usb: chipidea: udc: fix misuse of REGS_SHARED and PULLUP_ON_VBUS flags CI_HDRC_REGS_SHARED stands for the controller registers is shared with other USB drivers, if all USB drivers are at chipidea/, it doesn't needed to be set. CI_HDRC_PULLUP_ON_VBUS stands for pullup dp when the vbus is on. This flag doesn't need to be set if the vbus is always on for gadget since dp has always pulled up after the gadget has initialized. So, the current code seems to misuse this two flags. - When the gadget initializes, the controller doesn't need to run if it depends on vbus (CI_HDRC_PULLUP_ON_VBUS), it does not relate to shared register. - When the gadget starts (load one gadget module), the controller can run if vbus is on (CI_HDRC_PULLUP_ON_VBUS), it also does not relate to shared register. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index c70ce3891d35..45abf4d46a71 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1637,8 +1637,7 @@ static int ci_udc_start(struct usb_gadget *gadget, pm_runtime_get_sync(&ci->gadget.dev); if (ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) { if (ci->vbus_active) { - if (ci->platdata->flags & CI_HDRC_REGS_SHARED) - hw_device_reset(ci, USBMODE_CM_DC); + hw_device_reset(ci, USBMODE_CM_DC); } else { pm_runtime_put_sync(&ci->gadget.dev); goto done; @@ -1801,7 +1800,7 @@ static int udc_start(struct ci_hdrc *ci) } } - if (!(ci->platdata->flags & CI_HDRC_REGS_SHARED)) { + if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) { retval = hw_device_reset(ci, USBMODE_CM_DC); if (retval) goto put_transceiver; -- cgit v1.2.3-58-ga151 From d268e9bc23f5df4a2f4167da246d277c2a468d3d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:14 +0300 Subject: usb: chipidea: retire flag CI_HDRC_PULLUP_ON_VBUS Currently, the controller only runs when the ci->vbus_active is true. So the flag CI_HDRC_PULLUP_ON_VBUS is useless no longer. If the user doesn't have otgsc, he/she needs to change ci_handle_vbus_change to update ci->vbus_active. Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 1 - drivers/usb/chipidea/ci_hdrc_msm.c | 1 - drivers/usb/chipidea/udc.c | 24 ++++++------------------ include/linux/usb/chipidea.h | 1 - 4 files changed, 6 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 30fdc2fd8771..cc53e63c9ce9 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -88,7 +88,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) .name = "ci_hdrc_imx", .capoffset = DEF_CAPOFFSET, .flags = CI_HDRC_REQUIRE_TRANSCEIVER | - CI_HDRC_PULLUP_ON_VBUS | CI_HDRC_DISABLE_STREAMING, }; int ret; diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index fb657ef50a9c..2d51d852b474 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -49,7 +49,6 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = { .name = "ci_hdrc_msm", .flags = CI_HDRC_REGS_SHARED | CI_HDRC_REQUIRE_TRANSCEIVER | - CI_HDRC_PULLUP_ON_VBUS | CI_HDRC_DISABLE_STREAMING, .notify_event = ci_hdrc_msm_notify_event, diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 45abf4d46a71..90c3572bd1b0 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1448,9 +1448,6 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) unsigned long flags; int gadget_ready = 0; - if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) - return -EOPNOTSUPP; - spin_lock_irqsave(&ci->lock, flags); ci->vbus_active = is_active; if (ci->driver) @@ -1635,13 +1632,11 @@ static int ci_udc_start(struct usb_gadget *gadget, ci->driver = driver; pm_runtime_get_sync(&ci->gadget.dev); - if (ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) { - if (ci->vbus_active) { - hw_device_reset(ci, USBMODE_CM_DC); - } else { - pm_runtime_put_sync(&ci->gadget.dev); - goto done; - } + if (ci->vbus_active) { + hw_device_reset(ci, USBMODE_CM_DC); + } else { + pm_runtime_put_sync(&ci->gadget.dev); + goto done; } retval = hw_device_state(ci, ci->ep0out->qh.dma); @@ -1664,8 +1659,7 @@ static int ci_udc_stop(struct usb_gadget *gadget, spin_lock_irqsave(&ci->lock, flags); - if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) || - ci->vbus_active) { + if (ci->vbus_active) { hw_device_state(ci, 0); if (ci->platdata->notify_event) ci->platdata->notify_event(ci, @@ -1800,12 +1794,6 @@ static int udc_start(struct ci_hdrc *ci) } } - if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) { - retval = hw_device_reset(ci, USBMODE_CM_DC); - if (retval) - goto put_transceiver; - } - if (ci->transceiver) { retval = otg_set_peripheral(ci->transceiver->otg, &ci->gadget); diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 10a607ce9090..7d399671a566 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -18,7 +18,6 @@ struct ci_hdrc_platform_data { unsigned long flags; #define CI_HDRC_REGS_SHARED BIT(0) #define CI_HDRC_REQUIRE_TRANSCEIVER BIT(1) -#define CI_HDRC_PULLUP_ON_VBUS BIT(2) #define CI_HDRC_DISABLE_STREAMING BIT(3) /* * Only set it when DCCPARAMS.DC==1 and DCCPARAMS.HC==1, -- cgit v1.2.3-58-ga151 From 4a64783b66bbeed06a1f10ab56a1dd401dd5bfba Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:15 +0300 Subject: usb: chipidea: udc: .pullup is valid only when vbus is there For chipidea, the IP must know vbus before the controller begins to run. So the .pullup should only be called when the vbus is there. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 90c3572bd1b0..6b4c2f2eb946 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1511,6 +1511,9 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); + if (!ci->vbus_active) + return -EOPNOTSUPP; + if (is_on) hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); else -- cgit v1.2.3-58-ga151 From 05986ba9b025ae7742744e1108bd59b1f6e8f443 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 14 Aug 2013 12:44:16 +0300 Subject: USB: chipidea: i.MX: simplify usbmisc The chipidea i.MX driver is split into two drivers. The ci_hdrc_imx driver handles the chipidea cores and the usbmisc_imx driver handles the noncore registers common to all chipidea cores (but SoC specific). Current flow is: - usbmisc sets an ops pointer in the ci_hdrc_imx driver during probe - ci_hdrc_imx checks if the pointer is valid during probe, if yes calls the functions in the ops pointer. - usbmisc_imx calls back into the ci_hdrc_imx driver to get additional data This is overly complicated and has problems if the drivers are compiled as modules. In this case the usbmisc_imx driver can be unloaded even if the ci_hdrc_imx driver still needs usbmisc functionality. This patch changes this by letting the ci_hdrc_imx driver calling functions from the usbmisc_imx driver. This way the symbol resolving during module load makes sure the ci_hdrc_imx driver depends on the usbmisc_imx driver. Also instead of letting the usbmisc_imx driver call back into the ci_hdrc_imx driver, pass the needed data in the first place. Signed-off-by: Sascha Hauer Acked-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 71 +++++++++++++--------------- drivers/usb/chipidea/ci_hdrc_imx.h | 17 ++----- drivers/usb/chipidea/usbmisc_imx.c | 95 +++++++++++++++++--------------------- 3 files changed, 76 insertions(+), 107 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index cc53e63c9ce9..74d998d9b45b 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -27,57 +27,48 @@ struct ci_hdrc_imx_data { struct usb_phy *phy; struct platform_device *ci_pdev; struct clk *clk; + struct imx_usbmisc_data *usbmisc_data; }; -static const struct usbmisc_ops *usbmisc_ops; - /* Common functions shared by usbmisc drivers */ -int usbmisc_set_ops(const struct usbmisc_ops *ops) -{ - if (usbmisc_ops) - return -EBUSY; - - usbmisc_ops = ops; - - return 0; -} -EXPORT_SYMBOL_GPL(usbmisc_set_ops); - -void usbmisc_unset_ops(const struct usbmisc_ops *ops) -{ - usbmisc_ops = NULL; -} -EXPORT_SYMBOL_GPL(usbmisc_unset_ops); - -int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev) +static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) { struct device_node *np = dev->of_node; struct of_phandle_args args; + struct imx_usbmisc_data *data; int ret; - usbdev->dev = dev; + /* + * In case the fsl,usbmisc property is not present this device doesn't + * need usbmisc. Return NULL (which is no error here) + */ + if (!of_get_property(np, "fsl,usbmisc", NULL)) + return NULL; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 0, &args); if (ret) { dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", ret); - memset(usbdev, 0, sizeof(*usbdev)); - return ret; + return ERR_PTR(ret); } - usbdev->index = args.args[0]; + + data->index = args.args[0]; of_node_put(args.np); if (of_find_property(np, "disable-over-current", NULL)) - usbdev->disable_oc = 1; + data->disable_oc = 1; if (of_find_property(np, "external-vbus-divider", NULL)) - usbdev->evdo = 1; + data->evdo = 1; - return 0; + return data; } -EXPORT_SYMBOL_GPL(usbmisc_get_init_data); /* End of common functions shared by usbmisc drivers*/ @@ -92,16 +83,16 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) }; int ret; - if (of_find_property(pdev->dev.of_node, "fsl,usbmisc", NULL) - && !usbmisc_ops) - return -EPROBE_DEFER; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) { dev_err(&pdev->dev, "Failed to allocate ci_hdrc-imx data!\n"); return -ENOMEM; } + data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); + if (IS_ERR(data->usbmisc_data)) + return PTR_ERR(data->usbmisc_data); + data->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(data->clk)) { dev_err(&pdev->dev, @@ -135,11 +126,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (!pdev->dev.coherent_dma_mask) pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - if (usbmisc_ops && usbmisc_ops->init) { - ret = usbmisc_ops->init(&pdev->dev); + if (data->usbmisc_data) { + ret = imx_usbmisc_init(data->usbmisc_data); if (ret) { - dev_err(&pdev->dev, - "usbmisc init failed, ret=%d\n", ret); + dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", + ret); goto err_clk; } } @@ -155,11 +146,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) goto err_clk; } - if (usbmisc_ops && usbmisc_ops->post) { - ret = usbmisc_ops->post(&pdev->dev); + if (data->usbmisc_data) { + ret = imx_usbmisc_init_post(data->usbmisc_data); if (ret) { - dev_err(&pdev->dev, - "usbmisc post failed, ret=%d\n", ret); + dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", + ret); goto disable_device; } } diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 550bfa457620..c7271590dd0a 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -9,23 +9,12 @@ * http://www.gnu.org/copyleft/gpl.html */ -/* Used to set SoC specific callbacks */ -struct usbmisc_ops { - /* It's called once when probe a usb device */ - int (*init)(struct device *dev); - /* It's called once after adding a usb device */ - int (*post)(struct device *dev); -}; - -struct usbmisc_usb_device { - struct device *dev; /* usb controller device */ +struct imx_usbmisc_data { int index; unsigned int disable_oc:1; /* over current detect disabled */ unsigned int evdo:1; /* set external vbus divider option */ }; -int usbmisc_set_ops(const struct usbmisc_ops *ops); -void usbmisc_unset_ops(const struct usbmisc_ops *ops); -int -usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev); +int imx_usbmisc_init(struct imx_usbmisc_data *); +int imx_usbmisc_init_post(struct imx_usbmisc_data *); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index ac5a46155200..8a1094b1182f 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -18,8 +18,6 @@ #include "ci_hdrc_imx.h" -#define USB_DEV_MAX 4 - #define MX25_USB_PHY_CTRL_OFFSET 0x08 #define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23) @@ -32,51 +30,34 @@ #define MX6_BM_OVER_CUR_DIS BIT(7) +struct usbmisc_ops { + /* It's called once when probe a usb device */ + int (*init)(struct imx_usbmisc_data *data); + /* It's called once after adding a usb device */ + int (*post)(struct imx_usbmisc_data *data); +}; + struct imx_usbmisc { void __iomem *base; spinlock_t lock; struct clk *clk; - struct usbmisc_usb_device usbdev[USB_DEV_MAX]; const struct usbmisc_ops *ops; }; static struct imx_usbmisc *usbmisc; -static struct usbmisc_usb_device *get_usbdev(struct device *dev) -{ - int i, ret; - - for (i = 0; i < USB_DEV_MAX; i++) { - if (usbmisc->usbdev[i].dev == dev) - return &usbmisc->usbdev[i]; - else if (!usbmisc->usbdev[i].dev) - break; - } - - if (i >= USB_DEV_MAX) - return ERR_PTR(-EBUSY); - - ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]); - if (ret) - return ERR_PTR(ret); - - return &usbmisc->usbdev[i]; -} - -static int usbmisc_imx25_post(struct device *dev) +static int usbmisc_imx25_post(struct imx_usbmisc_data *data) { - struct usbmisc_usb_device *usbdev; void __iomem *reg; unsigned long flags; u32 val; - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); + if (data->index > 2) + return -EINVAL; reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; - if (usbdev->evdo) { + if (data->evdo) { spin_lock_irqsave(&usbmisc->lock, flags); val = readl(reg); writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg); @@ -87,20 +68,18 @@ static int usbmisc_imx25_post(struct device *dev) return 0; } -static int usbmisc_imx53_init(struct device *dev) +static int usbmisc_imx53_init(struct imx_usbmisc_data *data) { - struct usbmisc_usb_device *usbdev; void __iomem *reg = NULL; unsigned long flags; u32 val = 0; - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); + if (data->index > 3) + return -EINVAL; - if (usbdev->disable_oc) { + if (data->disable_oc) { spin_lock_irqsave(&usbmisc->lock, flags); - switch (usbdev->index) { + switch (data->index) { case 0: reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG; @@ -126,22 +105,19 @@ static int usbmisc_imx53_init(struct device *dev) return 0; } -static int usbmisc_imx6q_init(struct device *dev) +static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) { - - struct usbmisc_usb_device *usbdev; unsigned long flags; u32 reg; - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); + if (data->index > 3) + return -EINVAL; - if (usbdev->disable_oc) { + if (data->disable_oc) { spin_lock_irqsave(&usbmisc->lock, flags); - reg = readl(usbmisc->base + usbdev->index * 4); + reg = readl(usbmisc->base + data->index * 4); writel(reg | MX6_BM_OVER_CUR_DIS, - usbmisc->base + usbdev->index * 4); + usbmisc->base + data->index * 4); spin_unlock_irqrestore(&usbmisc->lock, flags); } @@ -160,6 +136,26 @@ static const struct usbmisc_ops imx6q_usbmisc_ops = { .init = usbmisc_imx6q_init, }; +int imx_usbmisc_init(struct imx_usbmisc_data *data) +{ + if (!usbmisc) + return -EPROBE_DEFER; + if (!usbmisc->ops->init) + return 0; + return usbmisc->ops->init(data); +} +EXPORT_SYMBOL_GPL(imx_usbmisc_init); + +int imx_usbmisc_init_post(struct imx_usbmisc_data *data) +{ + if (!usbmisc) + return -EPROBE_DEFER; + if (!usbmisc->ops->post) + return 0; + return usbmisc->ops->post(data); +} +EXPORT_SYMBOL_GPL(imx_usbmisc_init_post); + static const struct of_device_id usbmisc_imx_dt_ids[] = { { .compatible = "fsl,imx25-usbmisc", @@ -216,19 +212,12 @@ static int usbmisc_imx_probe(struct platform_device *pdev) of_match_device(usbmisc_imx_dt_ids, &pdev->dev); data->ops = (const struct usbmisc_ops *)tmp_dev->data; usbmisc = data; - ret = usbmisc_set_ops(data->ops); - if (ret) { - usbmisc = NULL; - clk_disable_unprepare(data->clk); - return ret; - } return 0; } static int usbmisc_imx_remove(struct platform_device *pdev) { - usbmisc_unset_ops(usbmisc->ops); clk_disable_unprepare(usbmisc->clk); usbmisc = NULL; return 0; -- cgit v1.2.3-58-ga151 From 2fcd1c9b327c23414a5215a0fa53df9d75871a46 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Aug 2013 13:27:36 +0200 Subject: USB: keyspan: fix serial DMA-buffer allocations Make sure serial DMA-buffers are allocated separately from containing structure to prevent potential memory corruption on non-cache-coherent systems. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 5a979729f8ec..3d2ce56e1009 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -56,17 +56,17 @@ struct keyspan_serial_private { const struct keyspan_device_details *device_details; struct urb *instat_urb; - char instat_buf[INSTAT_BUFLEN]; + char *instat_buf; /* added to support 49wg, where data from all 4 ports comes in on 1 EP and high-speed supported */ struct urb *indat_urb; - char indat_buf[INDAT49W_BUFLEN]; + char *indat_buf; /* XXX this one probably will need a lock */ struct urb *glocont_urb; - char glocont_buf[GLOCONT_BUFLEN]; - char ctrl_buf[8]; /* for EP0 control message */ + char *glocont_buf; + char *ctrl_buf; /* for EP0 control message */ }; struct keyspan_port_private { @@ -2313,6 +2313,22 @@ static int keyspan_startup(struct usb_serial *serial) return -ENOMEM; } + s_priv->instat_buf = kzalloc(INSTAT_BUFLEN, GFP_KERNEL); + if (!s_priv->instat_buf) + goto err_instat_buf; + + s_priv->indat_buf = kzalloc(INDAT49W_BUFLEN, GFP_KERNEL); + if (!s_priv->indat_buf) + goto err_indat_buf; + + s_priv->glocont_buf = kzalloc(GLOCONT_BUFLEN, GFP_KERNEL); + if (!s_priv->glocont_buf) + goto err_glocont_buf; + + s_priv->ctrl_buf = kzalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!s_priv->ctrl_buf) + goto err_ctrl_buf; + s_priv->device_details = d_details; usb_set_serial_data(serial, s_priv); @@ -2330,6 +2346,17 @@ static int keyspan_startup(struct usb_serial *serial) } return 0; + +err_ctrl_buf: + kfree(s_priv->glocont_buf); +err_glocont_buf: + kfree(s_priv->indat_buf); +err_indat_buf: + kfree(s_priv->instat_buf); +err_instat_buf: + kfree(s_priv); + + return -ENOMEM; } static void keyspan_disconnect(struct usb_serial *serial) @@ -2353,6 +2380,11 @@ static void keyspan_release(struct usb_serial *serial) usb_free_urb(s_priv->indat_urb); usb_free_urb(s_priv->glocont_urb); + kfree(s_priv->ctrl_buf); + kfree(s_priv->glocont_buf); + kfree(s_priv->indat_buf); + kfree(s_priv->instat_buf); + kfree(s_priv); } -- cgit v1.2.3-58-ga151 From bad41a5bf1775cdf6026d5a1b233bdc4853f27ca Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Aug 2013 13:27:37 +0200 Subject: USB: keyspan: fix port DMA-buffer allocations Make sure port DMA-buffers are allocated separately from containing structure to prevent potential memory corruption on non-cache-coherent systems. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 64 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 3d2ce56e1009..e731bbc166a0 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -50,6 +50,10 @@ #define INSTAT_BUFLEN 32 #define GLOCONT_BUFLEN 64 #define INDAT49W_BUFLEN 512 +#define IN_BUFLEN 64 +#define OUT_BUFLEN 64 +#define INACK_BUFLEN 1 +#define OUTCONT_BUFLEN 64 /* Per device and per port private data */ struct keyspan_serial_private { @@ -81,18 +85,18 @@ struct keyspan_port_private { /* Input endpoints and buffer for this port */ struct urb *in_urbs[2]; - char in_buffer[2][64]; + char *in_buffer[2]; /* Output endpoints and buffer for this port */ struct urb *out_urbs[2]; - char out_buffer[2][64]; + char *out_buffer[2]; /* Input ack endpoint */ struct urb *inack_urb; - char inack_buffer[1]; + char *inack_buffer; /* Output control endpoint */ struct urb *outcont_urb; - char outcont_buffer[64]; + char *outcont_buffer; /* Settings for the port */ int baud; @@ -2406,6 +2410,26 @@ static int keyspan_port_probe(struct usb_serial_port *port) if (!p_priv) return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i) { + p_priv->in_buffer[i] = kzalloc(IN_BUFLEN, GFP_KERNEL); + if (!p_priv->in_buffer[i]) + goto err_in_buffer; + } + + for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i) { + p_priv->out_buffer[i] = kzalloc(OUT_BUFLEN, GFP_KERNEL); + if (!p_priv->out_buffer[i]) + goto err_out_buffer; + } + + p_priv->inack_buffer = kzalloc(INACK_BUFLEN, GFP_KERNEL); + if (!p_priv->inack_buffer) + goto err_inack_buffer; + + p_priv->outcont_buffer = kzalloc(OUTCONT_BUFLEN, GFP_KERNEL); + if (!p_priv->outcont_buffer) + goto err_outcont_buffer; + p_priv->device_details = d_details; /* Setup values for the various callback routines */ @@ -2418,7 +2442,8 @@ static int keyspan_port_probe(struct usb_serial_port *port) for (i = 0; i <= d_details->indat_endp_flip; ++i, ++endp) { p_priv->in_urbs[i] = keyspan_setup_urb(serial, endp, USB_DIR_IN, port, - p_priv->in_buffer[i], 64, + p_priv->in_buffer[i], + IN_BUFLEN, cback->indat_callback); } /* outdat endpoints also have flip */ @@ -2426,25 +2451,41 @@ static int keyspan_port_probe(struct usb_serial_port *port) for (i = 0; i <= d_details->outdat_endp_flip; ++i, ++endp) { p_priv->out_urbs[i] = keyspan_setup_urb(serial, endp, USB_DIR_OUT, port, - p_priv->out_buffer[i], 64, + p_priv->out_buffer[i], + OUT_BUFLEN, cback->outdat_callback); } /* inack endpoint */ p_priv->inack_urb = keyspan_setup_urb(serial, d_details->inack_endpoints[port_num], USB_DIR_IN, port, - p_priv->inack_buffer, 1, + p_priv->inack_buffer, + INACK_BUFLEN, cback->inack_callback); /* outcont endpoint */ p_priv->outcont_urb = keyspan_setup_urb(serial, d_details->outcont_endpoints[port_num], USB_DIR_OUT, port, - p_priv->outcont_buffer, 64, + p_priv->outcont_buffer, + OUTCONT_BUFLEN, cback->outcont_callback); usb_set_serial_port_data(port, p_priv); return 0; + +err_outcont_buffer: + kfree(p_priv->inack_buffer); +err_inack_buffer: + for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i) + kfree(p_priv->out_buffer[i]); +err_out_buffer: + for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i) + kfree(p_priv->in_buffer[i]); +err_in_buffer: + kfree(p_priv); + + return -ENOMEM; } static int keyspan_port_remove(struct usb_serial_port *port) @@ -2468,6 +2509,13 @@ static int keyspan_port_remove(struct usb_serial_port *port) usb_free_urb(p_priv->out_urbs[i]); } + kfree(p_priv->outcont_buffer); + kfree(p_priv->inack_buffer); + for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i) + kfree(p_priv->out_buffer[i]); + for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i) + kfree(p_priv->in_buffer[i]); + kfree(p_priv); return 0; -- cgit v1.2.3-58-ga151 From 0448067150dc1e4034b018da253b0ed1c57d16c9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Aug 2013 13:27:38 +0200 Subject: USB: quatech2: fix serial DMA-buffer allocations Make sure serial DMA-buffers are allocated separately from containing structure to prevent potential memory corruption on non-cache-coherent systems. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index d99743290fc1..79c9b2be2edb 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -62,6 +62,7 @@ #define MAX_BAUD_RATE 921600 #define DEFAULT_BAUD_RATE 9600 +#define QT2_READ_BUFFER_SIZE 512 /* size of read buffer */ #define QT2_WRITE_BUFFER_SIZE 512 /* size of write buffer */ #define QT2_WRITE_CONTROL_SIZE 5 /* control bytes used for a write */ @@ -112,7 +113,7 @@ struct qt2_serial_private { unsigned char current_port; /* current port for incoming data */ struct urb *read_urb; /* shared among all ports */ - char read_buffer[512]; + char *read_buffer; }; struct qt2_port_private { @@ -142,6 +143,7 @@ static void qt2_release(struct usb_serial *serial) serial_priv = usb_get_serial_data(serial); usb_free_urb(serial_priv->read_urb); + kfree(serial_priv->read_buffer); kfree(serial_priv); } @@ -683,7 +685,7 @@ static int qt2_setup_urbs(struct usb_serial *serial) usb_rcvbulkpipe(serial->dev, port0->bulk_in_endpointAddress), serial_priv->read_buffer, - sizeof(serial_priv->read_buffer), + QT2_READ_BUFFER_SIZE, qt2_read_bulk_callback, serial); status = usb_submit_urb(serial_priv->read_urb, GFP_KERNEL); @@ -718,6 +720,12 @@ static int qt2_attach(struct usb_serial *serial) return -ENOMEM; } + serial_priv->read_buffer = kmalloc(QT2_READ_BUFFER_SIZE, GFP_KERNEL); + if (!serial_priv->read_buffer) { + status = -ENOMEM; + goto err_buf; + } + usb_set_serial_data(serial, serial_priv); status = qt2_setup_urbs(serial); @@ -727,6 +735,8 @@ static int qt2_attach(struct usb_serial *serial) return 0; attach_failed: + kfree(serial_priv->read_buffer); +err_buf: kfree(serial_priv); return status; } -- cgit v1.2.3-58-ga151 From cbf30a914e89883457512f01c6434f70e6d57d4d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Aug 2013 13:27:39 +0200 Subject: USB: quatech2: fix port DMA-buffer allocations Make sure serial DMA-buffers are allocated separately from containing structure to prevent potential memory corruption on non-cache-coherent systems. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 79c9b2be2edb..a24d59ae4032 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -122,7 +122,7 @@ struct qt2_port_private { spinlock_t urb_lock; bool urb_in_use; struct urb *write_urb; - char write_buffer[QT2_WRITE_BUFFER_SIZE]; + char *write_buffer; spinlock_t lock; u8 shadowLSR; @@ -755,21 +755,29 @@ static int qt2_port_probe(struct usb_serial_port *port) spin_lock_init(&port_priv->urb_lock); port_priv->port = port; + port_priv->write_buffer = kmalloc(QT2_WRITE_BUFFER_SIZE, GFP_KERNEL); + if (!port_priv->write_buffer) + goto err_buf; + port_priv->write_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!port_priv->write_urb) { - kfree(port_priv); - return -ENOMEM; - } + if (!port_priv->write_urb) + goto err_urb; + bEndpointAddress = serial->port[0]->bulk_out_endpointAddress; usb_fill_bulk_urb(port_priv->write_urb, serial->dev, usb_sndbulkpipe(serial->dev, bEndpointAddress), port_priv->write_buffer, - sizeof(port_priv->write_buffer), + QT2_WRITE_BUFFER_SIZE, qt2_write_bulk_callback, port); usb_set_serial_port_data(port, port_priv); return 0; +err_urb: + kfree(port_priv->write_buffer); +err_buf: + kfree(port_priv); + return -ENOMEM; } static int qt2_port_remove(struct usb_serial_port *port) @@ -778,6 +786,7 @@ static int qt2_port_remove(struct usb_serial_port *port) port_priv = usb_get_serial_port_data(port); usb_free_urb(port_priv->write_urb); + kfree(port_priv->write_buffer); kfree(port_priv); return 0; -- cgit v1.2.3-58-ga151 From 9821aa9de97ccaaa16297d42a2c5a532c0032097 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Aug 2013 13:27:40 +0200 Subject: USB: uss720: fix DMA-buffer allocation Make sure the USB control request is allocated separately from containing structure to prevent potential memory corruption on non-cache-coherent systems. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index e129cf661223..40ef40affe83 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -75,7 +75,7 @@ struct uss720_async_request { struct list_head asynclist; struct completion compl; struct urb *urb; - struct usb_ctrlrequest dr; + struct usb_ctrlrequest *dr; __u8 reg[7]; }; @@ -98,6 +98,7 @@ static void destroy_async(struct kref *kref) if (likely(rq->urb)) usb_free_urb(rq->urb); + kfree(rq->dr); spin_lock_irqsave(&priv->asynclock, flags); list_del_init(&rq->asynclist); spin_unlock_irqrestore(&priv->asynclock, flags); @@ -120,7 +121,7 @@ static void async_complete(struct urb *urb) if (status) { dev_err(&urb->dev->dev, "async_complete: urb error %d\n", status); - } else if (rq->dr.bRequest == 3) { + } else if (rq->dr->bRequest == 3) { memcpy(priv->reg, rq->reg, sizeof(priv->reg)); #if 0 dev_dbg(&priv->usbdev->dev, @@ -152,7 +153,7 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p usbdev = priv->usbdev; if (!usbdev) return NULL; - rq = kmalloc(sizeof(struct uss720_async_request), mem_flags); + rq = kzalloc(sizeof(struct uss720_async_request), mem_flags); if (!rq) { dev_err(&usbdev->dev, "submit_async_request out of memory\n"); return NULL; @@ -168,13 +169,18 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p dev_err(&usbdev->dev, "submit_async_request out of memory\n"); return NULL; } - rq->dr.bRequestType = requesttype; - rq->dr.bRequest = request; - rq->dr.wValue = cpu_to_le16(value); - rq->dr.wIndex = cpu_to_le16(index); - rq->dr.wLength = cpu_to_le16((request == 3) ? sizeof(rq->reg) : 0); + rq->dr = kmalloc(sizeof(*rq->dr), mem_flags); + if (!rq->dr) { + kref_put(&rq->ref_count, destroy_async); + return NULL; + } + rq->dr->bRequestType = requesttype; + rq->dr->bRequest = request; + rq->dr->wValue = cpu_to_le16(value); + rq->dr->wIndex = cpu_to_le16(index); + rq->dr->wLength = cpu_to_le16((request == 3) ? sizeof(rq->reg) : 0); usb_fill_control_urb(rq->urb, usbdev, (requesttype & 0x80) ? usb_rcvctrlpipe(usbdev, 0) : usb_sndctrlpipe(usbdev, 0), - (unsigned char *)&rq->dr, + (unsigned char *)rq->dr, (request == 3) ? rq->reg : NULL, (request == 3) ? sizeof(rq->reg) : 0, async_complete, rq); /* rq->urb->transfer_flags |= URB_ASYNC_UNLINK; */ spin_lock_irqsave(&priv->asynclock, flags); -- cgit v1.2.3-58-ga151 From aa5ceae24bf8dff1d6fe87c6c4b08e69c6d33550 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 30 Jul 2013 15:39:02 -0400 Subject: USB: handle LPM errors during device suspend correctly The hub driver's usb_port_suspend() routine doesn't handle errors related to Link Power Management properly. It always returns failure, it doesn't try to clean up the wakeup setting, (in the case of system sleep) it doesn't try to go ahead with the port suspend regardless, and it doesn't try to apply the new power-off mechanism. This patch fixes these problems. Note: Sarah fixed this patch to apply against 3.11, since the original commit (4fae6f0fa86f92e6bc7429371b1e177ad0aaac66 "USB: handle LPM errors during device suspend correctly") called usb_disable_remote_wakeup, which won't be added until 3.12. This patch should be backported to kernels as old as 3.5, that contain the commit 8306095fd2c1100e8244c09bf560f97aca5a311d "USB: Disable USB 3.0 LPM in critical sections.". There will be merge conflicts, since LTM wasn't added until 3.6. Signed-off-by: Alan Stern Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/hub.c | 71 +++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 33 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 558313de4911..8287953e066c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2918,7 +2918,6 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) { struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); struct usb_port *port_dev = hub->ports[udev->portnum - 1]; - enum pm_qos_flags_status pm_qos_stat; int port1 = udev->portnum; int status; bool really_suspend = true; @@ -2956,7 +2955,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) status); /* bail if autosuspend is requested */ if (PMSG_IS_AUTO(msg)) - return status; + goto err_wakeup; } } @@ -2965,14 +2964,16 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) usb_set_usb2_hardware_lpm(udev, 0); if (usb_disable_ltm(udev)) { - dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.", - __func__); - return -ENOMEM; + dev_err(&udev->dev, "Failed to disable LTM before suspend\n."); + status = -ENOMEM; + if (PMSG_IS_AUTO(msg)) + goto err_ltm; } if (usb_unlocked_disable_lpm(udev)) { - dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", - __func__); - return -ENOMEM; + dev_err(&udev->dev, "Failed to disable LPM before suspend\n."); + status = -ENOMEM; + if (PMSG_IS_AUTO(msg)) + goto err_lpm3; } /* see 7.1.7.6 */ @@ -3000,28 +3001,31 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (status) { dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); - /* paranoia: "should not happen" */ - if (udev->do_remote_wakeup) { - if (!hub_is_superspeed(hub->hdev)) { - (void) usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, - USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); - } else - (void) usb_disable_function_remotewakeup(udev); - - } + /* Try to enable USB3 LPM and LTM again */ + usb_unlocked_enable_lpm(udev); + err_lpm3: + usb_enable_ltm(udev); + err_ltm: /* Try to enable USB2 hardware LPM again */ if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LTM and LPM again */ - usb_enable_ltm(udev); - usb_unlocked_enable_lpm(udev); + if (udev->do_remote_wakeup) { + if (udev->speed < USB_SPEED_SUPER) + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, + USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT); + else + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, + USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT); + } + err_wakeup: /* System sleep transitions should never fail */ if (!PMSG_IS_AUTO(msg)) @@ -3043,14 +3047,15 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) * Check whether current status meets the requirement of * usb port power off mechanism */ - pm_qos_stat = dev_pm_qos_flags(&port_dev->dev, - PM_QOS_FLAG_NO_POWER_OFF); - if (!udev->do_remote_wakeup - && pm_qos_stat != PM_QOS_FLAGS_ALL - && udev->persist_enabled - && !status) { - pm_runtime_put_sync(&port_dev->dev); - port_dev->did_runtime_put = true; + if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled) { + enum pm_qos_flags_status pm_qos_stat; + + pm_qos_stat = dev_pm_qos_flags(&port_dev->dev, + PM_QOS_FLAG_NO_POWER_OFF); + if (pm_qos_stat != PM_QOS_FLAGS_ALL) { + pm_runtime_put_sync(&port_dev->dev); + port_dev->did_runtime_put = true; + } } usb_mark_last_busy(hub->hdev); -- cgit v1.2.3-58-ga151 From 98a4f1ff7bea8002ab79d6776e30d27932e88244 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Wed, 3 Jul 2013 22:17:54 +0800 Subject: usb: don't check pm qos NO_POWER_OFF flag in usb_port_suspend() The pm qos NO_POWER_OFF flag is checked twice during usb device suspend to see if the usb port power off condition is met. This is redundant and also will prevent the port from being powered off if the NO_POWER_OFF flag is changed to 1 from 0 after the device was already suspended. More detail in the following link. http://marc.info/?l=linux-usb&m=136543949130865&w=2 This patch should be backported to kernels as old as 3.7, that contain the commit f7ac7787ad361e31a7972e2854ed8dc2eedfac3b "usb/acpi: Use ACPI methods to power off ports." Signed-off-by: Lan Tianyu Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/hub.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 8287953e066c..17c3785241a4 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3043,19 +3043,9 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) usb_set_device_state(udev, USB_STATE_SUSPENDED); } - /* - * Check whether current status meets the requirement of - * usb port power off mechanism - */ if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled) { - enum pm_qos_flags_status pm_qos_stat; - - pm_qos_stat = dev_pm_qos_flags(&port_dev->dev, - PM_QOS_FLAG_NO_POWER_OFF); - if (pm_qos_stat != PM_QOS_FLAGS_ALL) { - pm_runtime_put_sync(&port_dev->dev); - port_dev->did_runtime_put = true; - } + pm_runtime_put_sync(&port_dev->dev); + port_dev->did_runtime_put = true; } usb_mark_last_busy(hub->hdev); -- cgit v1.2.3-58-ga151 From d49dad3e11638f66be4e16573ffaa8c46a09e3b3 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 5 Aug 2013 18:58:15 -0700 Subject: usb: Don't fail port power resume on device disconnect. Userspace can tell the kernel to power off any USB port, including ones that are visible and connectible to users. When an attached USB device goes into suspend, the port will be powered off if the pm_qos_no_port_poweroff file for its port is set to 0, the device does not have remote wakeup enabled, and the device is marked as persistent. If the user disconnects the USB device while the port is powered off, the current code does not handle that properly. If you disconnect a device, and then run `lsusb -v -s` for the device, the device disconnect does not get handled by the USB core. The runtime resume of the port fails, because hub_port_debounce_be_connected() returns -ETIMEDOUT. This means the port resume fails and khubd doesn't handle the USB device disconnect. This leaves the device listed in lsusb, and the port's runtime_status will be permanently marked as "error". Fix this by ignoring the return value of hub_port_debounce_be_connected. Users can disconnect USB devices while the ports are powered off, and we must be able to handle that. This patch should be backported to kernels as old as 3.9, that contain the commit ad493e5e580546e6c3024b76a41535476da1546a "usb: add usb port auto power off mechanism" Signed-off-by: Sarah Sharp Cc: Lan Tianyu Cc: Alan Stern Cc: Rafael J. Wysocki Cc: stable@vger.kernel.org Signed-off-by: Sarah Sharp --- drivers/usb/core/port.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index d6b0fadf53e9..9909911665ce 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -89,22 +89,19 @@ static int usb_port_runtime_resume(struct device *dev) retval = usb_hub_set_port_power(hdev, hub, port1, true); if (port_dev->child && !retval) { /* - * Wait for usb hub port to be reconnected in order to make - * the resume procedure successful. + * Attempt to wait for usb hub port to be reconnected in order + * to make the resume procedure successful. The device may have + * disconnected while the port was powered off, so ignore the + * return status. */ retval = hub_port_debounce_be_connected(hub, port1); - if (retval < 0) { + if (retval < 0) dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n", retval); - goto out; - } usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE); - - /* Set return value to 0 if debounce successful */ retval = 0; } -out: clear_bit(port1, hub->busy_bits); usb_autopm_put_interface(intf); return retval; -- cgit v1.2.3-58-ga151 From 52fb61250a7a132b0cfb9f4a1060a1f3c49e5a25 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 8 Aug 2013 10:08:34 -0700 Subject: xhci-plat: Don't enable legacy PCI interrupts. The xHCI platform driver calls into usb_add_hcd to register the irq for its platform device. It does not want the xHCI generic driver to register an interrupt for it at all. The original code did that by setting the XHCI_BROKEN_MSI quirk, which tells the xHCI driver to not enable MSI or MSI-X for a PCI host. Unfortunately, if CONFIG_PCI is enabled, and CONFIG_USB_DW3 is enabled, the xHCI generic driver will attempt to register a legacy PCI interrupt for the xHCI platform device in xhci_try_enable_msi(). This will result in a bogus irq being registered, since the underlying device is a platform_device, not a pci_device, and thus the pci_device->irq pointer will be bogus. Add a new quirk, XHCI_PLAT, so that the xHCI generic driver can distinguish between a PCI device that can't handle MSI or MSI-X, and a platform device that should not have its interrupts touched at all. This quirk may be useful in the future, in case other corner cases like this arise. This patch should be backported to kernels as old as 3.9, that contain the commit 00eed9c814cb8f281be6f0f5d8f45025dc0a97eb "USB: xhci: correctly enable interrupts". Signed-off-by: Sarah Sharp Reported-by: Yu Y Wang Tested-by: Yu Y Wang Reviewed-by: Felipe Balbi Cc: stable@vger.kernel.org --- drivers/usb/host/xhci-plat.c | 2 +- drivers/usb/host/xhci.c | 7 ++++++- drivers/usb/host/xhci.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 51e22bf89505..6eca5a536c0a 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -24,7 +24,7 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) * here that the generic code does not try to make a pci_dev from our * dev struct in order to setup MSI */ - xhci->quirks |= XHCI_BROKEN_MSI; + xhci->quirks |= XHCI_PLAT; } /* called during probe() after chip reset completes */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 9478caa2f71f..ead3555d6dba 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -343,9 +343,14 @@ static void __maybe_unused xhci_msix_sync_irqs(struct xhci_hcd *xhci) static int xhci_try_enable_msi(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + struct pci_dev *pdev; int ret; + /* The xhci platform device has set up IRQs through usb_add_hcd. */ + if (xhci->quirks & XHCI_PLAT) + return 0; + + pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); /* * Some Fresco Logic host controllers advertise MSI, but fail to * generate interrupts. Don't even try to enable MSI. diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index c338741a675d..6ab1e600dc18 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1542,6 +1542,7 @@ struct xhci_hcd { #define XHCI_SPURIOUS_REBOOT (1 << 13) #define XHCI_COMP_MODE_QUIRK (1 << 14) #define XHCI_AVOID_BEI (1 << 15) +#define XHCI_PLAT (1 << 16) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ -- cgit v1.2.3-58-ga151 From 6d33f7bb8c3863e54f8bdede0a2bf97a3585ac20 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Thu, 15 Aug 2013 12:21:30 -0500 Subject: USB: WUSBCORE: clear RPIPE stall for control endpoints When the HWA encounters a STALL on a control endpoint, it should clear the RPIPE_STALL feature on the RPIPE before processing the next transfer request. Otherwise, all transfer requests on that endpoint after the first STALL will fail because the RPIPE is still in the halted state. This also removes the unneccessary call to spin_lock_irqsave for a nested lock that was present in the first patch. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-hc.h | 15 +++++++- drivers/usb/wusbcore/wa-rpipe.c | 21 +++++++++++ drivers/usb/wusbcore/wa-xfer.c | 84 ++++++++++++++++++++++++++++++++++++----- include/linux/usb/wusb-wa.h | 1 + 4 files changed, 109 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h index d6bea3e0b54a..cf250c21e946 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h @@ -91,6 +91,7 @@ struct wusbhc; struct wahc; extern void wa_urb_enqueue_run(struct work_struct *ws); +extern void wa_process_errored_transfers_run(struct work_struct *ws); /** * RPipe instance @@ -190,8 +191,14 @@ struct wahc { struct list_head xfer_list; struct list_head xfer_delayed_list; + struct list_head xfer_errored_list; + /* + * lock for the above xfer lists. Can be taken while a xfer->lock is + * held but not in the reverse order. + */ spinlock_t xfer_list_lock; - struct work_struct xfer_work; + struct work_struct xfer_enqueue_work; + struct work_struct xfer_error_work; atomic_t xfer_id_count; }; @@ -244,8 +251,10 @@ static inline void wa_init(struct wahc *wa) edc_init(&wa->dti_edc); INIT_LIST_HEAD(&wa->xfer_list); INIT_LIST_HEAD(&wa->xfer_delayed_list); + INIT_LIST_HEAD(&wa->xfer_errored_list); spin_lock_init(&wa->xfer_list_lock); - INIT_WORK(&wa->xfer_work, wa_urb_enqueue_run); + INIT_WORK(&wa->xfer_enqueue_work, wa_urb_enqueue_run); + INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run); atomic_set(&wa->xfer_id_count, 1); } @@ -269,6 +278,8 @@ static inline void rpipe_put(struct wa_rpipe *rpipe) } extern void rpipe_ep_disable(struct wahc *, struct usb_host_endpoint *); +extern void rpipe_clear_feature_stalled(struct wahc *, + struct usb_host_endpoint *); extern int wa_rpipes_create(struct wahc *); extern void wa_rpipes_destroy(struct wahc *); static inline void rpipe_avail_dec(struct wa_rpipe *rpipe) diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c index 9a595c1ed867..fd4f1ce6256a 100644 --- a/drivers/usb/wusbcore/wa-rpipe.c +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -527,3 +527,24 @@ void rpipe_ep_disable(struct wahc *wa, struct usb_host_endpoint *ep) mutex_unlock(&wa->rpipe_mutex); } EXPORT_SYMBOL_GPL(rpipe_ep_disable); + +/* Clear the stalled status of an RPIPE. */ +void rpipe_clear_feature_stalled(struct wahc *wa, struct usb_host_endpoint *ep) +{ + struct wa_rpipe *rpipe; + + mutex_lock(&wa->rpipe_mutex); + rpipe = ep->hcpriv; + if (rpipe != NULL) { + u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex); + + usb_control_msg( + wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, + RPIPE_STALL, index, NULL, 0, 1000); + } + mutex_unlock(&wa->rpipe_mutex); +} +EXPORT_SYMBOL_GPL(rpipe_clear_feature_stalled); + diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index f5c81afc6e96..d74fe1ae16ac 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -1100,7 +1100,7 @@ error_xfer_submit: */ void wa_urb_enqueue_run(struct work_struct *ws) { - struct wahc *wa = container_of(ws, struct wahc, xfer_work); + struct wahc *wa = container_of(ws, struct wahc, xfer_enqueue_work); struct wa_xfer *xfer, *next; struct urb *urb; LIST_HEAD(tmp_list); @@ -1125,6 +1125,49 @@ void wa_urb_enqueue_run(struct work_struct *ws) } EXPORT_SYMBOL_GPL(wa_urb_enqueue_run); +/* + * Process the errored transfers on the Wire Adapter outside of interrupt. + */ +void wa_process_errored_transfers_run(struct work_struct *ws) +{ + struct wahc *wa = container_of(ws, struct wahc, xfer_error_work); + struct wa_xfer *xfer, *next; + LIST_HEAD(tmp_list); + + pr_info("%s: Run delayed STALL processing.\n", __func__); + + /* Create a copy of the wa->xfer_errored_list while holding the lock */ + spin_lock_irq(&wa->xfer_list_lock); + list_cut_position(&tmp_list, &wa->xfer_errored_list, + wa->xfer_errored_list.prev); + spin_unlock_irq(&wa->xfer_list_lock); + + /* + * run rpipe_clear_feature_stalled from temp list without list lock + * held. + */ + list_for_each_entry_safe(xfer, next, &tmp_list, list_node) { + struct usb_host_endpoint *ep; + unsigned long flags; + struct wa_rpipe *rpipe; + + spin_lock_irqsave(&xfer->lock, flags); + ep = xfer->ep; + rpipe = ep->hcpriv; + spin_unlock_irqrestore(&xfer->lock, flags); + + /* clear RPIPE feature stalled without holding a lock. */ + rpipe_clear_feature_stalled(wa, ep); + + /* complete the xfer. This removes it from the tmp list. */ + wa_xfer_completion(xfer); + + /* check for work. */ + wa_xfer_delayed_run(rpipe); + } +} +EXPORT_SYMBOL_GPL(wa_process_errored_transfers_run); + /* * Submit a transfer to the Wire Adapter in a delayed way * @@ -1180,7 +1223,7 @@ int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep, spin_lock_irqsave(&wa->xfer_list_lock, my_flags); list_add_tail(&xfer->list_node, &wa->xfer_delayed_list); spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); - queue_work(wusbd, &wa->xfer_work); + queue_work(wusbd, &wa->xfer_enqueue_work); } else { wa_urb_enqueue_b(xfer); } @@ -1222,7 +1265,8 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb) xfer = urb->hcpriv; if (xfer == NULL) { - /* NOthing setup yet enqueue will see urb->status != + /* + * Nothing setup yet enqueue will see urb->status != * -EINPROGRESS (by hcd layer) and bail out with * error, no need to do completion */ @@ -1360,7 +1404,7 @@ static int wa_xfer_status_to_errno(u8 status) * * inbound transfers: need to schedule a DTI read * - * FIXME: this functio needs to be broken up in parts + * FIXME: this function needs to be broken up in parts */ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer) { @@ -1482,17 +1526,37 @@ error_submit_buf_in: seg->result = result; kfree(wa->buf_in_urb->sg); error_sg_alloc: + __wa_xfer_abort(xfer); error_complete: seg->status = WA_SEG_ERROR; xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - __wa_xfer_abort(xfer); done = __wa_xfer_is_done(xfer); - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); + /* + * queue work item to clear STALL for control endpoints. + * Otherwise, let endpoint_reset take care of it. + */ + if (((usb_status & 0x3f) == WA_XFER_STATUS_HALTED) && + usb_endpoint_xfer_control(&xfer->ep->desc) && + done) { + + dev_info(dev, "Control EP stall. Queue delayed work.\n"); + spin_lock_irq(&wa->xfer_list_lock); + /* remove xfer from xfer_list. */ + list_del(&xfer->list_node); + /* add xfer to xfer_errored_list. */ + list_add_tail(&xfer->list_node, &wa->xfer_errored_list); + spin_unlock_irq(&wa->xfer_list_lock); + spin_unlock_irqrestore(&xfer->lock, flags); + queue_work(wusbd, &wa->xfer_error_work); + } else { + spin_unlock_irqrestore(&xfer->lock, flags); + if (done) + wa_xfer_completion(xfer); + if (rpipe_ready) + wa_xfer_delayed_run(rpipe); + } + return; error_bad_seg: diff --git a/include/linux/usb/wusb-wa.h b/include/linux/usb/wusb-wa.h index 6be985b2a434..4ff744e2b678 100644 --- a/include/linux/usb/wusb-wa.h +++ b/include/linux/usb/wusb-wa.h @@ -66,6 +66,7 @@ enum { WA_ENABLE = 0x01, WA_RESET = 0x02, RPIPE_PAUSE = 0x1, + RPIPE_STALL = 0x2, }; /* Responses from Get Status request ([WUSB] section 8.3.1.6) */ -- cgit v1.2.3-58-ga151 From 11b1bf81bf68cab0ad7d7d4d08dbfb236d5cbcaa Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Thu, 15 Aug 2013 14:37:41 -0500 Subject: USB: WUSBCORE: fix resource cleanup in error path in __wa_xfer_setup_segs Use usb_free_urb instead of kfree in error path and point to the correct URB. Also remember to clean up the sg list for the URB if it was allocated. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index d74fe1ae16ac..75a2bea62102 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -804,15 +804,17 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) return 0; error_sg_alloc: - kfree(seg->dto_urb); + usb_free_urb(xfer->seg[cnt]->dto_urb); error_dto_alloc: kfree(xfer->seg[cnt]); cnt--; error_seg_kzalloc: /* use the fact that cnt is left at were it failed */ for (; cnt >= 0; cnt--) { - if (xfer->seg[cnt] && xfer->is_inbound == 0) + if (xfer->seg[cnt] && xfer->is_inbound == 0) { usb_free_urb(xfer->seg[cnt]->dto_urb); + kfree(xfer->seg[cnt]->dto_urb->sg); + } kfree(xfer->seg[cnt]); } error_segs_kzalloc: -- cgit v1.2.3-58-ga151 From 79731cbd783d3693d4b84731e3cfc79b9f828170 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Thu, 15 Aug 2013 14:37:42 -0500 Subject: USB: WUSBCORE: fix leak of urb in wa_xfer_destroy. The check to free the URB was the opposite of the correct case. This patch removes the check altogether since the ptr will be NULL if the URB was not allocated. Also use usb_free_urb instead of usb_put_urb. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 75a2bea62102..956aab51cbda 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -166,8 +166,8 @@ static inline void wa_xfer_init(struct wa_xfer *xfer) /* * Destroy a transfer structure * - * Note that the xfer->seg[index] thingies follow the URB life cycle, - * so we need to put them, not free them. + * Note that freeing xfer->seg[cnt]->urb will free the containing + * xfer->seg[cnt] memory that was allocated by __wa_xfer_setup_segs. */ static void wa_xfer_destroy(struct kref *_xfer) { @@ -175,9 +175,8 @@ static void wa_xfer_destroy(struct kref *_xfer) if (xfer->seg) { unsigned cnt; for (cnt = 0; cnt < xfer->segs; cnt++) { - if (xfer->is_inbound) - usb_put_urb(xfer->seg[cnt]->dto_urb); - usb_put_urb(&xfer->seg[cnt]->urb); + usb_free_urb(xfer->seg[cnt]->dto_urb); + usb_free_urb(&xfer->seg[cnt]->urb); } } kfree(xfer); -- cgit v1.2.3-58-ga151 From 66591015d88d0b7fc88da79e2054830094ace4c9 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Thu, 15 Aug 2013 14:37:43 -0500 Subject: USB: WUSBCORE: Use usb_init_urb instead of creating the URB manually In wa_seg_init, use usb_init_urb to init the URB object contained in the transfer segment instead of initializing it manually. Use kmalloc to allocate the memory for segment instead of kzalloc and then use memset to set the non-URB portion of the transfer segment struct to 0 since that was already done by usb_init_urb. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 956aab51cbda..86dd3b65692f 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -125,10 +125,13 @@ struct wa_seg { u8 xfer_extra[]; /* xtra space for xfer_hdr_ctl */ }; -static void wa_seg_init(struct wa_seg *seg) +static inline void wa_seg_init(struct wa_seg *seg) { - /* usb_init_urb() repeats a lot of work, so we do it here */ - kref_init(&seg->urb.kref); + usb_init_urb(&seg->urb); + + /* set the remaining memory to 0. */ + memset(((void *)seg) + sizeof(seg->urb), 0, + sizeof(*seg) - sizeof(seg->urb)); } /* @@ -731,9 +734,9 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) buf_itr = 0; buf_size = xfer->urb->transfer_buffer_length; for (cnt = 0; cnt < xfer->segs; cnt++) { - seg = xfer->seg[cnt] = kzalloc(alloc_size, GFP_ATOMIC); + seg = xfer->seg[cnt] = kmalloc(alloc_size, GFP_ATOMIC); if (seg == NULL) - goto error_seg_kzalloc; + goto error_seg_kmalloc; wa_seg_init(seg); seg->xfer = xfer; seg->index = cnt; @@ -807,7 +810,7 @@ error_sg_alloc: error_dto_alloc: kfree(xfer->seg[cnt]); cnt--; -error_seg_kzalloc: +error_seg_kmalloc: /* use the fact that cnt is left at were it failed */ for (; cnt >= 0; cnt--) { if (xfer->seg[cnt] && xfer->is_inbound == 0) { -- cgit v1.2.3-58-ga151 From 68c91d377c9bd14cbe35c647ed3b847f7862c958 Mon Sep 17 00:00:00 2001 From: Yann Droneaud Date: Sun, 18 Aug 2013 21:29:00 +0200 Subject: USB: serial: fix stringify operator in usb-serial-simple usb-serial-simple uses an unknown stringify macro that make all drivers being named "stringify(vendor)". This can be a problem when two drivers have the same (wrong) name: kernel: usbcore: registered new interface driver usb_serial_simple kernel: usbserial: USB Serial support registered for stringify(vendor) kernel Error: Driver 'stringify(vendor)' is already registered, aborting... kernel: usbserial: problem -16 when registering driver stringify(vendor) kernel: usbserial: USB Serial deregistering driver stringify(vendor) kernel: usbcore: deregistering interface driver usb_serial_simple Before the fix: $ strings drivers/usb/serial/usb-serial-simple.o usb_serial_simple stringify(vendor) After the fix: $ strings drivers/usb/serial/usb-serial-simple.o usb_serial_simple funsoft flashloader vivopay moto_modem hp4x suunto siemens_mpi This patch makes usb-serial-simple use the correct stringify operator. Signed-off-by: Yann Droneaud Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial-simple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index 6a06131f48f4..52eb91f2eb2a 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -29,7 +29,7 @@ static const struct usb_device_id vendor##_id_table[] = { \ static struct usb_serial_driver vendor##_device = { \ .driver = { \ .owner = THIS_MODULE, \ - .name = "stringify(vendor)", \ + .name = #vendor, \ }, \ .id_table = vendor##_id_table, \ .num_ports = 1, \ -- cgit v1.2.3-58-ga151 From 9c1d6962517e4a441dcfca480a4f3e1e73c84591 Mon Sep 17 00:00:00 2001 From: Philippe De Swert Date: Sun, 18 Aug 2013 13:51:43 +0300 Subject: usb:gadget Fix comment for pointer to configfs The documentation for the USB gadget fs is actually in Documentation/usb/gadget_configfs.txt. Signed-off-by: Philippe De Swert Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 1e3f52574367..bddd7ede5455 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -555,7 +555,7 @@ config USB_CONFIGFS specified simply by creating appropriate directories in configfs. Associating functions with configurations is done by creating appropriate symbolic links. - For more information see Documentation/usb/gadget-configfs.txt. + For more information see Documentation/usb/gadget_configfs.txt. config USB_CONFIGFS_SERIAL boolean "Generic serial bulk in/out" -- cgit v1.2.3-58-ga151 From d0bd9a41186e076ea543c397ad8a67a6cf604b55 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 16 Aug 2013 10:16:59 +0300 Subject: USB: mos7720: use GFP_ATOMIC under spinlock The write_parport_reg_nonblock() function shouldn't sleep because it's called with spinlocks held. Signed-off-by: Dan Carpenter Cc: stable@vger.kernel.org Acked-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7720.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index b01300164fc0..248fda6d06f8 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -374,7 +374,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, kfree(urbtrack); return -ENOMEM; } - urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_KERNEL); + urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_ATOMIC); if (!urbtrack->setup) { usb_free_urb(urbtrack->urb); kfree(urbtrack); -- cgit v1.2.3-58-ga151 From 3b716caf190ccc6f2a09387210e0e6a26c1d81a4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 19 Aug 2013 13:05:45 +0200 Subject: USB: mos7720: fix big-endian control requests Fix endianess bugs in parallel-port code which caused corrupt control-requests to be issued on big-endian machines. Reported-by: kbuild test robot Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7720.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 248fda6d06f8..84657e07dc5d 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -382,8 +382,8 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, } urbtrack->setup->bRequestType = (__u8)0x40; urbtrack->setup->bRequest = (__u8)0x0e; - urbtrack->setup->wValue = get_reg_value(reg, dummy); - urbtrack->setup->wIndex = get_reg_index(reg); + urbtrack->setup->wValue = cpu_to_le16(get_reg_value(reg, dummy)); + urbtrack->setup->wIndex = cpu_to_le16(get_reg_index(reg)); urbtrack->setup->wLength = 0; usb_fill_control_urb(urbtrack->urb, usbdev, usb_sndctrlpipe(usbdev, 0), -- cgit v1.2.3-58-ga151 From f375fc520d4df0cd9fcb570f33c103c6c0311f9e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 21 Aug 2013 11:17:21 +0200 Subject: usb: ehci-mxc: check for pdata before dereferencing Commit 7e8d5cd93fac ("USB: Add EHCI support for MX27 and MX31 based boards") introduced code that could potentially lead to a NULL pointer dereference on driver removal. Fix this by checking for the value of pdata before dereferencing it. Signed-off-by: Daniel Mack Reported-by: Dan Carpenter Cc: stable # 2.6.33+ Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-mxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 5899ad6626c7..0528dc4526c8 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -184,7 +184,7 @@ static int ehci_mxc_drv_remove(struct platform_device *pdev) if (pdata && pdata->exit) pdata->exit(pdev); - if (pdata->otg) + if (pdata && pdata->otg) usb_phy_shutdown(pdata->otg); clk_disable_unprepare(priv->usbclk); -- cgit v1.2.3-58-ga151 From d0308d4b6b02597f39fc31a9bddf7bb3faad5622 Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Thu, 22 Aug 2013 14:49:38 +0200 Subject: usb: fix cleanup after failure in hub_configure() If the hub_configure() fails after setting the hdev->maxchild the hub->ports might be NULL or point to uninitialized kzallocated memory causing NULL pointer dereference in hub_quiesce() during cleanup. Now after such error the hdev->maxchild is set to 0 to avoid cleanup of uninitialized ports. Signed-off-by: Krzysztof Mazur Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 175179eb17ee..22811f1f86f3 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1573,6 +1573,7 @@ static int hub_configure(struct usb_hub *hub, return 0; fail: + hdev->maxchild = 0; dev_err (hub_dev, "config failed, %s (err %d)\n", message, ret); /* hub_disconnect() frees urb and descriptor */ -- cgit v1.2.3-58-ga151 From e58547eb9561a8a72d46e2d411090a614d33ac0e Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Thu, 22 Aug 2013 14:49:39 +0200 Subject: usb: fail on usb_hub_create_port_device() errors Ignoring usb_hub_create_port_device() errors cause later NULL pointer deference when uninitialized hub->ports[i] entries are dereferenced after port memory allocation error. Signed-off-by: Krzysztof Mazur Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 22811f1f86f3..79ec4a7640cd 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1562,10 +1562,15 @@ static int hub_configure(struct usb_hub *hub, if (hub->has_indicators && blinkenlights) hub->indicator [0] = INDICATOR_CYCLE; - for (i = 0; i < hdev->maxchild; i++) - if (usb_hub_create_port_device(hub, i + 1) < 0) + for (i = 0; i < hdev->maxchild; i++) { + ret = usb_hub_create_port_device(hub, i + 1); + if (ret < 0) { dev_err(hub->intfdev, "couldn't create port%d device.\n", i + 1); + hdev->maxchild = i; + goto fail_keep_maxchild; + } + } usb_hub_adjust_deviceremovable(hdev, hub->descriptor); @@ -1574,6 +1579,7 @@ static int hub_configure(struct usb_hub *hub, fail: hdev->maxchild = 0; +fail_keep_maxchild: dev_err (hub_dev, "config failed, %s (err %d)\n", message, ret); /* hub_disconnect() frees urb and descriptor */ -- cgit v1.2.3-58-ga151 From 3bbc47d8b07abad56780a8e214520259808c4f8b Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Thu, 22 Aug 2013 14:49:40 +0200 Subject: usb: don't use bNbrPorts after initialization After successful initialization hub->descriptor->bNbrPorts and hub->hdev->maxchild are equal, but using hub->hdev->maxchild is preferred because that value is explicitly used for initialization of hub->ports[]. Signed-off-by: Krzysztof Mazur Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 79ec4a7640cd..5d859fc07610 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -451,7 +451,7 @@ static void led_work (struct work_struct *work) if (hdev->state != USB_STATE_CONFIGURED || hub->quiescing) return; - for (i = 0; i < hub->descriptor->bNbrPorts; i++) { + for (i = 0; i < hdev->maxchild; i++) { unsigned selector, mode; /* 30%-50% duty cycle */ @@ -500,7 +500,7 @@ static void led_work (struct work_struct *work) } if (!changed && blinkenlights) { cursor++; - cursor %= hub->descriptor->bNbrPorts; + cursor %= hdev->maxchild; set_port_led(hub, cursor + 1, HUB_LED_GREEN); hub->indicator[cursor] = INDICATOR_CYCLE; changed++; @@ -830,7 +830,7 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay) else dev_dbg(hub->intfdev, "trying to enable port power on " "non-switchable hub\n"); - for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) + for (port1 = 1; port1 <= hub->hdev->maxchild; port1++) if (hub->ports[port1 - 1]->power_is_on) set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); else @@ -4648,9 +4648,7 @@ static void hub_events(void) hub_dev = hub->intfdev; intf = to_usb_interface(hub_dev); dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", - hdev->state, hub->descriptor - ? hub->descriptor->bNbrPorts - : 0, + hdev->state, hdev->maxchild, /* NOTE: expects max 15 ports... */ (u16) hub->change_bits[0], (u16) hub->event_bits[0]); @@ -4695,7 +4693,7 @@ static void hub_events(void) } /* deal with port status changes */ - for (i = 1; i <= hub->descriptor->bNbrPorts; i++) { + for (i = 1; i <= hdev->maxchild; i++) { if (test_bit(i, hub->busy_bits)) continue; connect_change = test_bit(i, hub->change_bits); -- cgit v1.2.3-58-ga151 From 598d03610a0169eb88d2b08d6743be31b3b9c258 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 23 Aug 2013 15:12:14 -0700 Subject: USB: core: use DRIVER_ATTR_RW() Use DRIVER_ATTR_RW() to make it easier to audit sysfs file permissions. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 124bcc50b0b7..f7841d44feda 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -94,34 +94,27 @@ ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf) } EXPORT_SYMBOL_GPL(usb_show_dynids); -static ssize_t show_dynids(struct device_driver *driver, char *buf) +static ssize_t new_id_show(struct device_driver *driver, char *buf) { struct usb_driver *usb_drv = to_usb_driver(driver); return usb_show_dynids(&usb_drv->dynids, buf); } -static ssize_t store_new_id(struct device_driver *driver, +static ssize_t new_id_store(struct device_driver *driver, const char *buf, size_t count) { struct usb_driver *usb_drv = to_usb_driver(driver); return usb_store_new_id(&usb_drv->dynids, driver, buf, count); } -static DRIVER_ATTR(new_id, S_IRUGO | S_IWUSR, show_dynids, store_new_id); +static DRIVER_ATTR_RW(new_id); -/** - * store_remove_id - remove a USB device ID from this driver - * @driver: target device driver - * @buf: buffer for scanning device ID data - * @count: input size - * - * Removes a dynamic usb device ID from this driver. - * - * Return: @count on success. A negative error code otherwise. +/* + * Remove a USB device ID from this driver */ -static ssize_t -store_remove_id(struct device_driver *driver, const char *buf, size_t count) +static ssize_t remove_id_store(struct device_driver *driver, const char *buf, + size_t count) { struct usb_dynid *dynid, *n; struct usb_driver *usb_driver = to_usb_driver(driver); @@ -146,7 +139,12 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) spin_unlock(&usb_driver->dynids.lock); return count; } -static DRIVER_ATTR(remove_id, S_IRUGO | S_IWUSR, show_dynids, store_remove_id); + +static ssize_t remove_id_show(struct device_driver *driver, char *buf) +{ + return new_id_show(driver, buf); +} +static DRIVER_ATTR_RW(remove_id); static int usb_create_newid_files(struct usb_driver *usb_drv) { -- cgit v1.2.3-58-ga151 From d03f254f2ee2a708af9a7347402d9aed7f6cc4c1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 23 Aug 2013 16:05:26 -0700 Subject: USB: core: be specific about attribute permissions Instead of having to audit all sysfs attributes, to ensure we get them right, use the default macros the driver core provides us (read-only, read-write) to make the code simpler, and to prevent any mistakes from ever happening. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/endpoint.c | 36 +++--- drivers/usb/core/hcd.c | 17 +-- drivers/usb/core/port.c | 7 +- drivers/usb/core/sysfs.c | 303 ++++++++++++++++++++------------------------ 4 files changed, 166 insertions(+), 197 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index f13a289a0743..39a24021fe4d 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -32,31 +32,31 @@ struct ep_attribute { container_of(_attr, struct ep_attribute, attr) #define usb_ep_attr(field, format_string) \ -static ssize_t show_ep_##field(struct device *dev, \ +static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct ep_device *ep = to_ep_device(dev); \ return sprintf(buf, format_string, ep->desc->field); \ } \ -static DEVICE_ATTR(field, S_IRUGO, show_ep_##field, NULL); +static DEVICE_ATTR_RO(field) -usb_ep_attr(bLength, "%02x\n") -usb_ep_attr(bEndpointAddress, "%02x\n") -usb_ep_attr(bmAttributes, "%02x\n") -usb_ep_attr(bInterval, "%02x\n") +usb_ep_attr(bLength, "%02x\n"); +usb_ep_attr(bEndpointAddress, "%02x\n"); +usb_ep_attr(bmAttributes, "%02x\n"); +usb_ep_attr(bInterval, "%02x\n"); -static ssize_t show_ep_wMaxPacketSize(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t wMaxPacketSize_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct ep_device *ep = to_ep_device(dev); return sprintf(buf, "%04x\n", usb_endpoint_maxp(ep->desc) & 0x07ff); } -static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL); +static DEVICE_ATTR_RO(wMaxPacketSize); -static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t type_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct ep_device *ep = to_ep_device(dev); char *type = "unknown"; @@ -77,10 +77,10 @@ static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr, } return sprintf(buf, "%s\n", type); } -static DEVICE_ATTR(type, S_IRUGO, show_ep_type, NULL); +static DEVICE_ATTR_RO(type); -static ssize_t show_ep_interval(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t interval_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct ep_device *ep = to_ep_device(dev); char unit; @@ -123,10 +123,10 @@ static ssize_t show_ep_interval(struct device *dev, return sprintf(buf, "%d%cs\n", interval, unit); } -static DEVICE_ATTR(interval, S_IRUGO, show_ep_interval, NULL); +static DEVICE_ATTR_RO(interval); -static ssize_t show_ep_direction(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t direction_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct ep_device *ep = to_ep_device(dev); char *direction; @@ -139,7 +139,7 @@ static ssize_t show_ep_direction(struct device *dev, direction = "out"; return sprintf(buf, "%s\n", direction); } -static DEVICE_ATTR(direction, S_IRUGO, show_ep_direction, NULL); +static DEVICE_ATTR_RO(direction); static struct attribute *ep_dev_attrs[] = { &dev_attr_bLength.attr, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 19ad3d2f8a24..d6a8d23f047b 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -849,9 +849,8 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) /* * Show & store the current value of authorized_default */ -static ssize_t usb_host_authorized_default_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t authorized_default_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_device *rh_usb_dev = to_usb_device(dev); struct usb_bus *usb_bus = rh_usb_dev->bus; @@ -863,9 +862,9 @@ static ssize_t usb_host_authorized_default_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default); } -static ssize_t usb_host_authorized_default_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) +static ssize_t authorized_default_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) { ssize_t result; unsigned val; @@ -885,11 +884,7 @@ static ssize_t usb_host_authorized_default_store(struct device *dev, result = -EINVAL; return result; } - -static DEVICE_ATTR(authorized_default, 0644, - usb_host_authorized_default_show, - usb_host_authorized_default_store); - +static DEVICE_ATTR_RW(authorized_default); /* Group all the USB bus attributes */ static struct attribute *usb_bus_attrs[] = { diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 9909911665ce..51542f852393 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -23,8 +23,8 @@ static const struct attribute_group *port_dev_group[]; -static ssize_t show_port_connect_type(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t connect_type_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_port *port_dev = to_usb_port(dev); char *result; @@ -46,8 +46,7 @@ static ssize_t show_port_connect_type(struct device *dev, return sprintf(buf, "%s\n", result); } -static DEVICE_ATTR(connect_type, S_IRUGO, show_port_connect_type, - NULL); +static DEVICE_ATTR_RO(connect_type); static struct attribute *port_dev_attrs[] = { &dev_attr_connect_type.attr, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index d9284b998bd7..6d2c8edb1ffe 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -18,8 +18,8 @@ /* Active configuration fields */ #define usb_actconfig_show(field, format_string) \ -static ssize_t show_##field(struct device *dev, \ - struct device_attribute *attr, char *buf) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ { \ struct usb_device *udev; \ struct usb_host_config *actconfig; \ @@ -35,12 +35,12 @@ static ssize_t show_##field(struct device *dev, \ #define usb_actconfig_attr(field, format_string) \ usb_actconfig_show(field, format_string) \ - static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); + static DEVICE_ATTR_RO(field) -usb_actconfig_attr(bNumInterfaces, "%2d\n") -usb_actconfig_attr(bmAttributes, "%2x\n") +usb_actconfig_attr(bNumInterfaces, "%2d\n"); +usb_actconfig_attr(bmAttributes, "%2x\n"); -static ssize_t show_bMaxPower(struct device *dev, +static ssize_t bMaxPower_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; @@ -52,9 +52,9 @@ static ssize_t show_bMaxPower(struct device *dev, return 0; return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig)); } -static DEVICE_ATTR(bMaxPower, S_IRUGO, show_bMaxPower, NULL); +static DEVICE_ATTR_RO(bMaxPower); -static ssize_t show_configuration_string(struct device *dev, +static ssize_t configuration_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; @@ -66,14 +66,14 @@ static ssize_t show_configuration_string(struct device *dev, return 0; return sprintf(buf, "%s\n", actconfig->string); } -static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL); +static DEVICE_ATTR_RO(configuration); /* configuration value is always present, and r/w */ usb_actconfig_show(bConfigurationValue, "%u\n"); -static ssize_t -set_bConfigurationValue(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t bConfigurationValue_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); int config, value; @@ -85,13 +85,12 @@ set_bConfigurationValue(struct device *dev, struct device_attribute *attr, usb_unlock_device(udev); return (value < 0) ? value : count; } - static DEVICE_ATTR_IGNORE_LOCKDEP(bConfigurationValue, S_IRUGO | S_IWUSR, - show_bConfigurationValue, set_bConfigurationValue); + bConfigurationValue_show, bConfigurationValue_store); /* String fields */ #define usb_string_attr(name) \ -static ssize_t show_##name(struct device *dev, \ +static ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_device *udev; \ @@ -103,14 +102,14 @@ static ssize_t show_##name(struct device *dev, \ usb_unlock_device(udev); \ return retval; \ } \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); +static DEVICE_ATTR_RO(name) usb_string_attr(product); usb_string_attr(manufacturer); usb_string_attr(serial); -static ssize_t -show_speed(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t speed_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev; char *speed; @@ -139,40 +138,40 @@ show_speed(struct device *dev, struct device_attribute *attr, char *buf) } return sprintf(buf, "%s\n", speed); } -static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL); +static DEVICE_ATTR_RO(speed); -static ssize_t -show_busnum(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t busnum_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev; udev = to_usb_device(dev); return sprintf(buf, "%d\n", udev->bus->busnum); } -static DEVICE_ATTR(busnum, S_IRUGO, show_busnum, NULL); +static DEVICE_ATTR_RO(busnum); -static ssize_t -show_devnum(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t devnum_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev; udev = to_usb_device(dev); return sprintf(buf, "%d\n", udev->devnum); } -static DEVICE_ATTR(devnum, S_IRUGO, show_devnum, NULL); +static DEVICE_ATTR_RO(devnum); -static ssize_t -show_devpath(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t devpath_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev; udev = to_usb_device(dev); return sprintf(buf, "%s\n", udev->devpath); } -static DEVICE_ATTR(devpath, S_IRUGO, show_devpath, NULL); +static DEVICE_ATTR_RO(devpath); -static ssize_t -show_version(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t version_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev; u16 bcdUSB; @@ -181,30 +180,30 @@ show_version(struct device *dev, struct device_attribute *attr, char *buf) bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB); return sprintf(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff); } -static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); +static DEVICE_ATTR_RO(version); -static ssize_t -show_maxchild(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t maxchild_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev; udev = to_usb_device(dev); return sprintf(buf, "%d\n", udev->maxchild); } -static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL); +static DEVICE_ATTR_RO(maxchild); -static ssize_t -show_quirks(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t quirks_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev; udev = to_usb_device(dev); return sprintf(buf, "0x%x\n", udev->quirks); } -static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL); +static DEVICE_ATTR_RO(quirks); -static ssize_t -show_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t avoid_reset_quirk_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_device *udev; @@ -212,9 +211,9 @@ show_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, char * return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET)); } -static ssize_t -set_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t avoid_reset_quirk_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); int val; @@ -229,22 +228,20 @@ set_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, usb_unlock_device(udev); return count; } +static DEVICE_ATTR_RW(avoid_reset_quirk); -static DEVICE_ATTR(avoid_reset_quirk, S_IRUGO | S_IWUSR, - show_avoid_reset_quirk, set_avoid_reset_quirk); - -static ssize_t -show_urbnum(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t urbnum_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev; udev = to_usb_device(dev); return sprintf(buf, "%d\n", atomic_read(&udev->urbnum)); } -static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); +static DEVICE_ATTR_RO(urbnum); -static ssize_t -show_removable(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t removable_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev; char *state; @@ -264,30 +261,29 @@ show_removable(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%s\n", state); } -static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL); +static DEVICE_ATTR_RO(removable); -static ssize_t -show_ltm_capable(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t ltm_capable_show(struct device *dev, + struct device_attribute *attr, char *buf) { if (usb_device_supports_ltm(to_usb_device(dev))) return sprintf(buf, "%s\n", "yes"); return sprintf(buf, "%s\n", "no"); } -static DEVICE_ATTR(ltm_capable, S_IRUGO, show_ltm_capable, NULL); +static DEVICE_ATTR_RO(ltm_capable); #ifdef CONFIG_PM -static ssize_t -show_persist(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t persist_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev = to_usb_device(dev); return sprintf(buf, "%d\n", udev->persist_enabled); } -static ssize_t -set_persist(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t persist_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); int value; @@ -304,8 +300,7 @@ set_persist(struct device *dev, struct device_attribute *attr, usb_unlock_device(udev); return count; } - -static DEVICE_ATTR(persist, S_IRUGO | S_IWUSR, show_persist, set_persist); +static DEVICE_ATTR_RW(persist); static int add_persist_attributes(struct device *dev) { @@ -340,17 +335,15 @@ static void remove_persist_attributes(struct device *dev) #ifdef CONFIG_PM_RUNTIME -static ssize_t -show_connected_duration(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t connected_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_device *udev = to_usb_device(dev); return sprintf(buf, "%u\n", jiffies_to_msecs(jiffies - udev->connect_time)); } - -static DEVICE_ATTR(connected_duration, S_IRUGO, show_connected_duration, NULL); +static DEVICE_ATTR_RO(connected_duration); /* * If the device is resumed, the last time the device was suspended has @@ -359,9 +352,8 @@ static DEVICE_ATTR(connected_duration, S_IRUGO, show_connected_duration, NULL); * * If the device is suspended, the active_duration is up-to-date. */ -static ssize_t -show_active_duration(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t active_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_device *udev = to_usb_device(dev); int duration; @@ -372,18 +364,17 @@ show_active_duration(struct device *dev, struct device_attribute *attr, duration = jiffies_to_msecs(udev->active_duration); return sprintf(buf, "%u\n", duration); } +static DEVICE_ATTR_RO(active_duration); -static DEVICE_ATTR(active_duration, S_IRUGO, show_active_duration, NULL); - -static ssize_t -show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t autosuspend_show(struct device *dev, + struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", dev->power.autosuspend_delay / 1000); } -static ssize_t -set_autosuspend(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t autosuspend_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) { int value; @@ -394,9 +385,7 @@ set_autosuspend(struct device *dev, struct device_attribute *attr, pm_runtime_set_autosuspend_delay(dev, value * 1000); return count; } - -static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, - show_autosuspend, set_autosuspend); +static DEVICE_ATTR_RW(autosuspend); static const char on_string[] = "on"; static const char auto_string[] = "auto"; @@ -411,8 +400,8 @@ static void warn_level(void) { } } -static ssize_t -show_level(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t level_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_device *udev = to_usb_device(dev); const char *p = auto_string; @@ -423,9 +412,8 @@ show_level(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%s\n", p); } -static ssize_t -set_level(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t level_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); int len = count; @@ -453,12 +441,10 @@ set_level(struct device *dev, struct device_attribute *attr, usb_unlock_device(udev); return rc; } +static DEVICE_ATTR_RW(level); -static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); - -static ssize_t -show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t usb2_hardware_lpm_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_device *udev = to_usb_device(dev); const char *p; @@ -471,9 +457,9 @@ show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%s\n", p); } -static ssize_t -set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t usb2_hardware_lpm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); bool value; @@ -493,21 +479,19 @@ set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, return ret; } +static DEVICE_ATTR_RW(usb2_hardware_lpm); -static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm, - set_usb2_hardware_lpm); - -static ssize_t -show_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t usb2_lpm_l1_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct usb_device *udev = to_usb_device(dev); return sprintf(buf, "%d\n", udev->l1_params.timeout); } -static ssize_t -set_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t usb2_lpm_l1_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); u16 timeout; @@ -519,21 +503,18 @@ set_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr, return count; } +static DEVICE_ATTR_RW(usb2_lpm_l1_timeout); -static DEVICE_ATTR(usb2_lpm_l1_timeout, S_IRUGO | S_IWUSR, - show_usb2_lpm_l1_timeout, set_usb2_lpm_l1_timeout); - -static ssize_t -show_usb2_lpm_besl(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t usb2_lpm_besl_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_device *udev = to_usb_device(dev); return sprintf(buf, "%d\n", udev->l1_params.besl); } -static ssize_t -set_usb2_lpm_besl(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t usb2_lpm_besl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); u8 besl; @@ -545,9 +526,7 @@ set_usb2_lpm_besl(struct device *dev, struct device_attribute *attr, return count; } - -static DEVICE_ATTR(usb2_lpm_besl, S_IRUGO | S_IWUSR, - show_usb2_lpm_besl, set_usb2_lpm_besl); +static DEVICE_ATTR_RW(usb2_lpm_besl); static struct attribute *usb2_hardware_lpm_attr[] = { &dev_attr_usb2_hardware_lpm.attr, @@ -604,7 +583,7 @@ static void remove_power_attributes(struct device *dev) /* Descriptor fields */ #define usb_descriptor_attr_le16(field, format_string) \ static ssize_t \ -show_##field(struct device *dev, struct device_attribute *attr, \ +field##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct usb_device *udev; \ @@ -613,15 +592,15 @@ show_##field(struct device *dev, struct device_attribute *attr, \ return sprintf(buf, format_string, \ le16_to_cpu(udev->descriptor.field)); \ } \ -static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); +static DEVICE_ATTR_RO(field) -usb_descriptor_attr_le16(idVendor, "%04x\n") -usb_descriptor_attr_le16(idProduct, "%04x\n") -usb_descriptor_attr_le16(bcdDevice, "%04x\n") +usb_descriptor_attr_le16(idVendor, "%04x\n"); +usb_descriptor_attr_le16(idProduct, "%04x\n"); +usb_descriptor_attr_le16(bcdDevice, "%04x\n"); #define usb_descriptor_attr(field, format_string) \ static ssize_t \ -show_##field(struct device *dev, struct device_attribute *attr, \ +field##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct usb_device *udev; \ @@ -629,34 +608,31 @@ show_##field(struct device *dev, struct device_attribute *attr, \ udev = to_usb_device(dev); \ return sprintf(buf, format_string, udev->descriptor.field); \ } \ -static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); - -usb_descriptor_attr(bDeviceClass, "%02x\n") -usb_descriptor_attr(bDeviceSubClass, "%02x\n") -usb_descriptor_attr(bDeviceProtocol, "%02x\n") -usb_descriptor_attr(bNumConfigurations, "%d\n") -usb_descriptor_attr(bMaxPacketSize0, "%d\n") +static DEVICE_ATTR_RO(field) +usb_descriptor_attr(bDeviceClass, "%02x\n"); +usb_descriptor_attr(bDeviceSubClass, "%02x\n"); +usb_descriptor_attr(bDeviceProtocol, "%02x\n"); +usb_descriptor_attr(bNumConfigurations, "%d\n"); +usb_descriptor_attr(bMaxPacketSize0, "%d\n"); /* show if the device is authorized (1) or not (0) */ -static ssize_t usb_dev_authorized_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t authorized_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_device *usb_dev = to_usb_device(dev); return snprintf(buf, PAGE_SIZE, "%u\n", usb_dev->authorized); } - /* * Authorize a device to be used in the system * * Writing a 0 deauthorizes the device, writing a 1 authorizes it. */ -static ssize_t usb_dev_authorized_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) +static ssize_t authorized_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) { ssize_t result; struct usb_device *usb_dev = to_usb_device(dev); @@ -670,14 +646,12 @@ static ssize_t usb_dev_authorized_store(struct device *dev, result = usb_authorize_device(usb_dev); return result < 0? result : size; } - -static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, 0644, - usb_dev_authorized_show, usb_dev_authorized_store); +static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR, + authorized_show, authorized_store); /* "Safely remove a device" */ -static ssize_t usb_remove_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t remove_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); int rc = 0; @@ -694,7 +668,7 @@ static ssize_t usb_remove_store(struct device *dev, usb_unlock_device(udev); return rc; } -static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0200, NULL, usb_remove_store); +static DEVICE_ATTR_IGNORE_LOCKDEP(remove, S_IWUSR, NULL, remove_store); static struct attribute *dev_attrs[] = { @@ -853,7 +827,7 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) /* Interface Accociation Descriptor fields */ #define usb_intf_assoc_attr(field, format_string) \ static ssize_t \ -show_iad_##field(struct device *dev, struct device_attribute *attr, \ +iad_##field##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ @@ -861,18 +835,18 @@ show_iad_##field(struct device *dev, struct device_attribute *attr, \ return sprintf(buf, format_string, \ intf->intf_assoc->field); \ } \ -static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL); +static DEVICE_ATTR_RO(iad_##field) -usb_intf_assoc_attr(bFirstInterface, "%02x\n") -usb_intf_assoc_attr(bInterfaceCount, "%02d\n") -usb_intf_assoc_attr(bFunctionClass, "%02x\n") -usb_intf_assoc_attr(bFunctionSubClass, "%02x\n") -usb_intf_assoc_attr(bFunctionProtocol, "%02x\n") +usb_intf_assoc_attr(bFirstInterface, "%02x\n"); +usb_intf_assoc_attr(bInterfaceCount, "%02d\n"); +usb_intf_assoc_attr(bFunctionClass, "%02x\n"); +usb_intf_assoc_attr(bFunctionSubClass, "%02x\n"); +usb_intf_assoc_attr(bFunctionProtocol, "%02x\n"); /* Interface fields */ #define usb_intf_attr(field, format_string) \ static ssize_t \ -show_##field(struct device *dev, struct device_attribute *attr, \ +field##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ @@ -880,17 +854,17 @@ show_##field(struct device *dev, struct device_attribute *attr, \ return sprintf(buf, format_string, \ intf->cur_altsetting->desc.field); \ } \ -static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); +static DEVICE_ATTR_RO(field) -usb_intf_attr(bInterfaceNumber, "%02x\n") -usb_intf_attr(bAlternateSetting, "%2d\n") -usb_intf_attr(bNumEndpoints, "%02x\n") -usb_intf_attr(bInterfaceClass, "%02x\n") -usb_intf_attr(bInterfaceSubClass, "%02x\n") -usb_intf_attr(bInterfaceProtocol, "%02x\n") +usb_intf_attr(bInterfaceNumber, "%02x\n"); +usb_intf_attr(bAlternateSetting, "%2d\n"); +usb_intf_attr(bNumEndpoints, "%02x\n"); +usb_intf_attr(bInterfaceClass, "%02x\n"); +usb_intf_attr(bInterfaceSubClass, "%02x\n"); +usb_intf_attr(bInterfaceProtocol, "%02x\n"); -static ssize_t show_interface_string(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t interface_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_interface *intf; char *string; @@ -903,10 +877,10 @@ static ssize_t show_interface_string(struct device *dev, return 0; return sprintf(buf, "%s\n", string); } -static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL); +static DEVICE_ATTR_RO(interface); -static ssize_t show_modalias(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_interface *intf; struct usb_device *udev; @@ -929,10 +903,11 @@ static ssize_t show_modalias(struct device *dev, alt->desc.bInterfaceProtocol, alt->desc.bInterfaceNumber); } -static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); +static DEVICE_ATTR_RO(modalias); -static ssize_t show_supports_autosuspend(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t supports_autosuspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct usb_interface *intf; struct usb_device *udev; @@ -952,7 +927,7 @@ static ssize_t show_supports_autosuspend(struct device *dev, return ret; } -static DEVICE_ATTR(supports_autosuspend, S_IRUGO, show_supports_autosuspend, NULL); +static DEVICE_ATTR_RO(supports_autosuspend); static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, -- cgit v1.2.3-58-ga151 From 2a6eb8ac9bdee5b09077ae2d6efc4babffd9d93e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 23 Aug 2013 16:09:33 -0700 Subject: USB: usbtmc: fix up attribute permissions In auditing the usbtmc sysfs files, a bunch of them were being created as "read only", yet they have logic to handle writing to. So fix them up by setting the permissions properly. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 66c4001306f0..09de131ee0cb 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -899,7 +899,7 @@ err_out: } #define capability_attribute(name) \ -static ssize_t show_##name(struct device *dev, \ +static ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ @@ -907,7 +907,7 @@ static ssize_t show_##name(struct device *dev, \ \ return sprintf(buf, "%d\n", data->capabilities.name); \ } \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) +static DEVICE_ATTR_RO(name) capability_attribute(interface_capabilities); capability_attribute(device_capabilities); @@ -926,7 +926,7 @@ static struct attribute_group capability_attr_grp = { .attrs = capability_attrs, }; -static ssize_t show_TermChar(struct device *dev, +static ssize_t TermChar_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -935,7 +935,7 @@ static ssize_t show_TermChar(struct device *dev, return sprintf(buf, "%c\n", data->TermChar); } -static ssize_t store_TermChar(struct device *dev, +static ssize_t TermChar_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -947,10 +947,10 @@ static ssize_t store_TermChar(struct device *dev, data->TermChar = buf[0]; return count; } -static DEVICE_ATTR(TermChar, S_IRUGO, show_TermChar, store_TermChar); +static DEVICE_ATTR_RW(TermChar); #define data_attribute(name) \ -static ssize_t show_##name(struct device *dev, \ +static ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ @@ -958,7 +958,7 @@ static ssize_t show_##name(struct device *dev, \ \ return sprintf(buf, "%d\n", data->name); \ } \ -static ssize_t store_##name(struct device *dev, \ +static ssize_t name##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ @@ -976,7 +976,7 @@ static ssize_t store_##name(struct device *dev, \ else \ return count; \ } \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, store_##name) +static DEVICE_ATTR_RW(name) data_attribute(TermCharEnabled); data_attribute(auto_abort); -- cgit v1.2.3-58-ga151 From 154547c4fe0fbe92185e69a6cdc2b0502b361995 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 23 Aug 2013 16:18:34 -0700 Subject: USB: serial: clean up attribute permissions Clean up the DEVICE_ATTR usage in the USB serial drivers, making them more obvious as to the permissions that the sysfs files should be. Note: ftdi_sio.c still has a DEVICE_ATTR() used, that will have to wait until after 3.12-rc1 comes out when DEVICE_ATTR_WO() shows up in Linus's tree. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/bus.c | 5 ++--- drivers/usb/serial/ftdi_sio.c | 15 ++++++--------- drivers/usb/serial/io_ti.c | 8 +++----- drivers/usb/serial/iuu_phoenix.c | 8 +++----- 4 files changed, 14 insertions(+), 22 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index f053b302a00d..0ce51f21ed2a 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -38,15 +38,14 @@ static int usb_serial_device_match(struct device *dev, return 0; } -static ssize_t show_port_number(struct device *dev, +static ssize_t port_number_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_serial_port *port = to_usb_serial_port(dev); return sprintf(buf, "%d\n", port->port_number); } - -static DEVICE_ATTR(port_number, S_IRUGO, show_port_number, NULL); +static DEVICE_ATTR_RO(port_number); static int usb_serial_device_probe(struct device *dev) { diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index dbf2f2818823..c45f9c0a1b34 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1561,8 +1561,8 @@ static void ftdi_set_max_packet_size(struct usb_serial_port *port) * *************************************************************************** */ -static ssize_t show_latency_timer(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t latency_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_serial_port *port = to_usb_serial_port(dev); struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1572,11 +1572,10 @@ static ssize_t show_latency_timer(struct device *dev, return sprintf(buf, "%i\n", priv->latency); } - /* Write a new value of the latency timer, in units of milliseconds. */ -static ssize_t store_latency_timer(struct device *dev, - struct device_attribute *attr, const char *valbuf, - size_t count) +static ssize_t latency_timer_store(struct device *dev, + struct device_attribute *attr, + const char *valbuf, size_t count) { struct usb_serial_port *port = to_usb_serial_port(dev); struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1589,6 +1588,7 @@ static ssize_t store_latency_timer(struct device *dev, return -EIO; return count; } +static DEVICE_ATTR_RW(latency_timer); /* Write an event character directly to the FTDI register. The ASCII value is in the low 8 bits, with the enable bit in the 9th bit. */ @@ -1616,9 +1616,6 @@ static ssize_t store_event_char(struct device *dev, return count; } - -static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, - store_latency_timer); static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char); static int create_sysfs_attrs(struct usb_serial_port *port) diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 9c18f5985c8d..b7187bf32469 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2484,7 +2484,7 @@ static int edge_port_remove(struct usb_serial_port *port) /* Sysfs Attributes */ -static ssize_t show_uart_mode(struct device *dev, +static ssize_t uart_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_serial_port *port = to_usb_serial_port(dev); @@ -2493,7 +2493,7 @@ static ssize_t show_uart_mode(struct device *dev, return sprintf(buf, "%d\n", edge_port->bUartMode); } -static ssize_t store_uart_mode(struct device *dev, +static ssize_t uart_mode_store(struct device *dev, struct device_attribute *attr, const char *valbuf, size_t count) { struct usb_serial_port *port = to_usb_serial_port(dev); @@ -2509,9 +2509,7 @@ static ssize_t store_uart_mode(struct device *dev, return count; } - -static DEVICE_ATTR(uart_mode, S_IWUSR | S_IRUGO, show_uart_mode, - store_uart_mode); +static DEVICE_ATTR_RW(uart_mode); static int edge_create_sysfs_attrs(struct usb_serial_port *port) { diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 790673e5faa7..57c439a24b5a 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -1130,7 +1130,7 @@ static int iuu_vcc_set(struct usb_serial_port *port, unsigned int vcc) * Sysfs Attributes */ -static ssize_t show_vcc_mode(struct device *dev, +static ssize_t vcc_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_serial_port *port = to_usb_serial_port(dev); @@ -1139,7 +1139,7 @@ static ssize_t show_vcc_mode(struct device *dev, return sprintf(buf, "%d\n", priv->vcc); } -static ssize_t store_vcc_mode(struct device *dev, +static ssize_t vcc_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_serial_port *port = to_usb_serial_port(dev); @@ -1163,9 +1163,7 @@ static ssize_t store_vcc_mode(struct device *dev, fail_store_vcc_mode: return count; } - -static DEVICE_ATTR(vcc_mode, S_IRUSR | S_IWUSR, show_vcc_mode, - store_vcc_mode); +static DEVICE_ATTR_RW(vcc_mode); static int iuu_create_sysfs_attrs(struct usb_serial_port *port) { -- cgit v1.2.3-58-ga151 From c8476fb855434c733099079063990e5bfa7ecad6 Mon Sep 17 00:00:00 2001 From: Shawn Nematbakhsh Date: Mon, 19 Aug 2013 10:36:13 -0700 Subject: usb: xhci: Disable runtime PM suspend for quirky controllers If a USB controller with XHCI_RESET_ON_RESUME goes to runtime suspend, a reset will be performed upon runtime resume. Any previously suspended devices attached to the controller will be re-enumerated at this time. This will cause problems, for example, if an open system call on the device triggered the resume (the open call will fail). Note that this change is only relevant when persist_enabled is not set for USB devices. This patch should be backported to kernels as old as 3.0, that contain the commit c877b3b2ad5cb9d4fe523c5496185cc328ff3ae9 "xhci: Add reset on resume quirk for asrock p67 host". Signed-off-by: Shawn Nematbakhsh Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/host/xhci.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ba0ec0a96481..349d3460a17a 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3557,10 +3557,21 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_virt_device *virt_dev; + struct device *dev = hcd->self.controller; unsigned long flags; u32 state; int i, ret; +#ifndef CONFIG_USB_DEFAULT_PERSIST + /* + * We called pm_runtime_get_noresume when the device was attached. + * Decrement the counter here to allow controller to runtime suspend + * if no devices remain. + */ + if (xhci->quirks & XHCI_RESET_ON_RESUME) + pm_runtime_put_noidle(dev); +#endif + ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); /* If the host is halted due to driver unload, we still need to free the * device. @@ -3634,6 +3645,7 @@ static int xhci_reserve_host_control_ep_resources(struct xhci_hcd *xhci) int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct device *dev = hcd->self.controller; unsigned long flags; int timeleft; int ret; @@ -3686,6 +3698,16 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) goto disable_slot; } udev->slot_id = xhci->slot_id; + +#ifndef CONFIG_USB_DEFAULT_PERSIST + /* + * If resetting upon resume, we can't put the controller into runtime + * suspend if there is a device attached. + */ + if (xhci->quirks & XHCI_RESET_ON_RESUME) + pm_runtime_get_noresume(dev); +#endif + /* Is this a LS or FS device under a HS hub? */ /* Hub or peripherial? */ return 1; -- cgit v1.2.3-58-ga151 From dcf06a036848b4e8e6c8220f2e00b9adf6f84918 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 21 Aug 2013 18:50:09 +0300 Subject: xhci: fix port BESL LPM capability checking Wrong capability bit was checked for best effort service latency. bit 20 indicate port is BESL LPM capable (BLC), bit 19 is hardware LPM capable (HLC) This patch should be backported to kernels as old as 3.11, that contain the commit a558ccdcc71c7770c5e80c926a31cfe8a3892a09 "usb: xhci: add USB2 Link power management BESL support" Signed-off-by: Mathias Nyman Signed-off-by: Sarah Sharp Reported-by: Steve Cotton Cc: stable@vger.kernel.org --- drivers/usb/host/xhci-ext-caps.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index 8d7a1324e2f3..9fe3225e6c61 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h @@ -71,7 +71,7 @@ /* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ #define XHCI_HLC (1 << 19) -#define XHCI_BLC (1 << 19) +#define XHCI_BLC (1 << 20) /* command register values to disable interrupts and halt the HC */ /* start/stop HC execution - do not write unless HC is halted*/ -- cgit v1.2.3-58-ga151 From 0730d52a86919300a39a2be37f6c140997dfb82f Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 27 Aug 2013 17:47:35 +0300 Subject: xhci:prevent "callbacks suppressed" when debug is not enabled When debug is not enabled and dev_dbg() will expand to nothing, log might be flooded with "callbacks suppressed". If it was not done on purpose, better to use dev_dbg_ratelimited() instead. Signed-off-by: Dmitry Kasatkin Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7b35af167e55..411da1fc7ae8 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3087,14 +3087,10 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, * to set the polling interval (once the API is added). */ if (xhci_interval != ep_interval) { - if (printk_ratelimit()) - dev_dbg(&urb->dev->dev, "Driver uses different interval" - " (%d microframe%s) than xHCI " - "(%d microframe%s)\n", - ep_interval, - ep_interval == 1 ? "" : "s", - xhci_interval, - xhci_interval == 1 ? "" : "s"); + dev_dbg_ratelimited(&urb->dev->dev, + "Driver uses different interval (%d microframe%s) than xHCI (%d microframe%s)\n", + ep_interval, ep_interval == 1 ? "" : "s", + xhci_interval, xhci_interval == 1 ? "" : "s"); urb->interval = xhci_interval; /* Convert back to frames for LS/FS devices */ if (urb->dev->speed == USB_SPEED_LOW || @@ -3876,14 +3872,10 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, * to set the polling interval (once the API is added). */ if (xhci_interval != ep_interval) { - if (printk_ratelimit()) - dev_dbg(&urb->dev->dev, "Driver uses different interval" - " (%d microframe%s) than xHCI " - "(%d microframe%s)\n", - ep_interval, - ep_interval == 1 ? "" : "s", - xhci_interval, - xhci_interval == 1 ? "" : "s"); + dev_dbg_ratelimited(&urb->dev->dev, + "Driver uses different interval (%d microframe%s) than xHCI (%d microframe%s)\n", + ep_interval, ep_interval == 1 ? "" : "s", + xhci_interval, xhci_interval == 1 ? "" : "s"); urb->interval = xhci_interval; /* Convert back to frames for LS/FS devices */ if (urb->dev->speed == USB_SPEED_LOW || -- cgit v1.2.3-58-ga151 From 5a68e9b57b1c1984dae8a9625bbf1a505d166035 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 8 Aug 2013 09:43:28 +0200 Subject: usb: gadget: configfs: keep a function if it is not successfully added If usb_add_function() fails then the currently processed function is already not in the list in struct config_usb_cfg, and neither is it in the list in struct usb_configuration. At the err_purge_funcs label the purge_config_funcs() is called, which iterates over all configurations, and in each configuration it iterates over all _successfully_ added functions, and moves them back from the list in struct usb_configuration to the list in struct config_usb_cfg. BUT the function which has just failed adding and caused the unwind process is not taken care of and is effectively lost. This patch modifies the configfs_composite_bind() function so that if the usb_add_function() fails, then the currently processed function is returned to the list in struct config_usb_cfg. It would be tempting to delay the list_del() in question after usb_add_function() invocation, but a struct list_head (&f->list) cannot be stored in more than one list at the same time, so the list_del() must be called before usb_add_function(). Hence, the solution is to list_add() after usb_add_function() in case of error. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/configfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 80e7f75a56c7..8f0d6141e5e6 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -859,8 +859,10 @@ static int configfs_composite_bind(struct usb_gadget *gadget, list_for_each_entry_safe(f, tmp, &cfg->func_list, list) { list_del(&f->list); ret = usb_add_function(c, f); - if (ret) + if (ret) { + list_add(&f->list, &cfg->func_list); goto err_purge_funcs; + } } usb_ep_autoconfig_reset(cdev->gadget); } -- cgit v1.2.3-58-ga151 From 7b360f4287e52802b8c62e0608737e8bbfda67e1 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 13 Aug 2013 19:35:43 +0200 Subject: usb: musb: honour the return value of dma_map_single() Since dma_map_single() may fail it is good to actually check the return code to see if it succeeded. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 4376f51f5ef1..696e9e0b05af 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -76,13 +76,21 @@ static inline void map_dma_buffer(struct musb_request *request, return; if (request->request.dma == DMA_ADDR_INVALID) { - request->request.dma = dma_map_single( + dma_addr_t dma_addr; + int ret; + + dma_addr = dma_map_single( musb->controller, request->request.buf, request->request.length, request->tx ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + ret = dma_mapping_error(musb->controller, dma_addr); + if (ret) + return; + + request->request.dma = dma_addr; request->map_state = MUSB_MAPPED; } else { dma_sync_single_for_device(musb->controller, -- cgit v1.2.3-58-ga151 From 4ff745710f830f53738324b88623a9b53f257f72 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 16 Aug 2013 17:32:49 +0200 Subject: usb: phy: am335x-control: make it compile with Randy reported this |drivers/usb/phy/phy-am335x-control.c:45:3: error: implicit declaration |of function '__WARN' [-Werror=implicit-function-declaration] and left it as an excercice to figure out that this happens only with CONFIG_BUG=n. As a fix I replace it with WARN_ON(). And there is a space before return so fix this, too. Reported-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-am335x-control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 759754521426..22cf07d62e4c 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -42,8 +42,8 @@ static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on) reg = AM335X_USB1_CTRL; break; default: - __WARN(); - return; + WARN_ON(1); + return; } val = readl(usb_ctrl->phy_reg + reg); -- cgit v1.2.3-58-ga151 From 0b5c1e62d80c8ee43b9f8435fe54ed097dba6a3c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 18 Aug 2013 16:13:13 +0800 Subject: usb: musb: cppi41: fix missing unlock on error in cppi41_dma_callback() Add the missing unlock before return from function cppi41_dma_callback() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index e64701d15401..ae959746f77f 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -150,8 +150,10 @@ static void cppi41_dma_callback(void *private_data) remain_bytes, direction, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (WARN_ON(!dma_desc)) + if (WARN_ON(!dma_desc)) { + spin_unlock_irqrestore(&musb->lock, flags); return; + } dma_desc->callback = cppi41_dma_callback; dma_desc->callback_param = channel; -- cgit v1.2.3-58-ga151 From b130f03fd2b156ba0d1f82f8b3d9624fe342753e Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Mon, 19 Aug 2013 08:24:08 +0200 Subject: usb: musb: avoid null pointer dereference in debug logging Since commit 511f3c53 (usb: gadget: udc-core: fix a regression during gadget driver unbinding) usb_gadget_remove_driver will pass NULL for the driver argument. Signed-off-by: Maarten ter Huurne Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 696e9e0b05af..b7b8b2bc4ee9 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1937,7 +1937,8 @@ static int musb_gadget_stop(struct usb_gadget *g, stop_activity(musb, driver); otg_set_peripheral(musb->xceiv->otg, NULL); - dev_dbg(musb->controller, "unregistering driver %s\n", driver->function); + dev_dbg(musb->controller, "unregistering driver %s\n", + driver ? driver->function : "(removed)"); musb->is_active = 0; musb->gadget_driver = NULL; -- cgit v1.2.3-58-ga151 From b2fb945d607ec33cbaf47ac1173fbd03511b4fb4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 18 Aug 2013 22:20:41 +0200 Subject: usb: gadget: USB_FUSB300 should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `fusb300_set_idma': drivers/usb/gadget/fusb300_udc.c:946: undefined reference to `usb_gadget_map_request' drivers/usb/gadget/fusb300_udc.c:958: undefined reference to `usb_gadget_unmap_request' Signed-off-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index bddd7ede5455..af19b562b6d9 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -187,7 +187,7 @@ config USB_FSL_USB2 config USB_FUSB300 tristate "Faraday FUSB300 USB Peripheral Controller" - depends on !PHYS_ADDR_T_64BIT + depends on !PHYS_ADDR_T_64BIT && HAS_DMA help Faraday usb device controller FUSB300 driver -- cgit v1.2.3-58-ga151 From 4ee4f23bb04ef6602910a0adbb0824094ffb4a7d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 18 Aug 2013 22:20:42 +0200 Subject: usb: gadget: USB_R8A66597 should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `sudmac_free_channel': drivers/usb/gadget/r8a66597-udc.c:676: undefined reference to `usb_gadget_unmap_request' drivers/built-in.o: In function `sudmac_alloc_channel': drivers/usb/gadget/r8a66597-udc.c:666: undefined reference to `usb_gadget_map_request' Signed-off-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index af19b562b6d9..e1b64c47d1f7 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -244,6 +244,7 @@ config USB_PXA25X_SMALL config USB_R8A66597 tristate "Renesas R8A66597 USB Peripheral Controller" + depends on HAS_DMA help R8A66597 is a discrete USB host and peripheral controller chip that supports both full and high speed USB 2.0 data transfers. -- cgit v1.2.3-58-ga151 From 272b05a9885bfe8575beac0e4fa1a1bc121f17ec Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 18 Aug 2013 22:20:43 +0200 Subject: usb: gadget: USB_NET2272_DMA should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `net2272_done': drivers/usb/gadget/net2272.c:386: undefined reference to `usb_gadget_unmap_request' drivers/built-in.o: In function `net2272_queue': drivers/usb/gadget/net2272.c:848: undefined reference to `usb_gadget_map_request' Signed-off-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index e1b64c47d1f7..30e2dd8a1f2c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -386,7 +386,7 @@ config USB_NET2272 config USB_NET2272_DMA boolean "Support external DMA controller" - depends on USB_NET2272 + depends on USB_NET2272 && HAS_DMA help The NET2272 part can optionally support an external DMA controller, but your board has to have support in the -- cgit v1.2.3-58-ga151 From 51ef74f6400c731827454d010b35b122103fe9f9 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 19 Aug 2013 18:00:08 +0200 Subject: usb: musb: dsps: fix devm_ioremap_resource error detection code devm_ioremap_resource returns an ERR_PTR value, not NULL, on failure. Furthermore, the value returned by devm_ioremap_resource should be tested. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression e,e1; statement S; @@ *e = devm_ioremap_resource(...); if (!e1) S // Signed-off-by: Julia Lawall Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 4ffbaace7913..4ad52e76c4dd 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -361,8 +361,8 @@ static int dsps_musb_init(struct musb *musb) return -EINVAL; reg_base = devm_ioremap_resource(dev, r); - if (!musb->ctrl_base) - return -EINVAL; + if (IS_ERR(reg_base)) + return PTR_ERR(reg_base); musb->ctrl_base = reg_base; /* NOP driver needs change if supporting dual instance */ -- cgit v1.2.3-58-ga151 From 9047428894660f8f46305917e519ab62f7395cac Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 20 Aug 2013 18:35:44 +0200 Subject: usb: musb: only remove host/udc if it has been added musb_shutdown() removes always USB host and device. musb_init_controller() adds host and device depending on port_mode. If port mode is set to HOST then the removal of UDC leads only to: |(NULL device *): gadget not registered. and nothing else happens. If port mode is set to DEVICE and we remove the host then we oops in usb_remove_hcd(). This patch ensures that we only remove host in OTG/host mode and device only in OTG/device mode to avoid any trouble. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 2 ++ drivers/usb/musb/musb_host.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index b7b8b2bc4ee9..9a08679d204d 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1812,6 +1812,8 @@ err: void musb_gadget_cleanup(struct musb *musb) { + if (musb->port_mode == MUSB_PORT_MODE_HOST) + return; usb_del_gadget_udc(&musb->g); } diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index a9695f5a92fb..9a2b8c85f19a 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2628,6 +2628,8 @@ int musb_host_alloc(struct musb *musb) void musb_host_cleanup(struct musb *musb) { + if (musb->port_mode == MUSB_PORT_MODE_GADGET) + return; usb_remove_hcd(musb->hcd); musb->hcd = NULL; } -- cgit v1.2.3-58-ga151 From c031a7d419340f30e66dc18fab4373ad646ca7e6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 20 Aug 2013 18:35:47 +0200 Subject: usb: usb: dsps: update code according to the binding document This relfects the code and dts requires changes due to recent .dts binding updates: - use mg prefix for the Metor Graphics specific attributes - use power in mA not in mA/2 as specifed in the USB2.0 specification - remove the child node for USB. This is driver specific on won't be reflected in the device tree - use the "mentor" prefix instead of "mg". - use "dr_mode" istead of "mg,port-mode" for the port mode. The former is used by a few other drivers. Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Stephen Warren Cc: Ian Campbell Cc: devicetree@vger.kernel.org Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am335x-bone.dts | 2 +- arch/arm/boot/dts/am335x-evm.dts | 6 ++-- arch/arm/boot/dts/am335x-evmsk.dts | 2 +- arch/arm/boot/dts/am33xx.dtsi | 67 +++++++++++++++----------------------- drivers/usb/musb/musb_dsps.c | 54 ++++++++++++++++++++---------- 5 files changed, 67 insertions(+), 64 deletions(-) (limited to 'drivers/usb') diff --git a/arch/arm/boot/dts/am335x-bone.dts b/arch/arm/boot/dts/am335x-bone.dts index a8907b57c75c..e8447a79dc7e 100644 --- a/arch/arm/boot/dts/am335x-bone.dts +++ b/arch/arm/boot/dts/am335x-bone.dts @@ -127,7 +127,7 @@ status = "okay"; }; - phy@47401300 { + usb-phy@47401300 { status = "okay"; }; diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index c26c16cace3c..648a67e74d1b 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -178,11 +178,11 @@ status = "okay"; }; - phy@47401300 { + usb-phy@47401300 { status = "okay"; }; - phy@47401b00 { + usb-phy@47401b00 { status = "okay"; }; @@ -194,7 +194,7 @@ status = "okay"; }; - dma@07402000 { + dma-controller@07402000 { status = "okay"; }; }; diff --git a/arch/arm/boot/dts/am335x-evmsk.dts b/arch/arm/boot/dts/am335x-evmsk.dts index e92446c6846e..a6c5033be442 100644 --- a/arch/arm/boot/dts/am335x-evmsk.dts +++ b/arch/arm/boot/dts/am335x-evmsk.dts @@ -214,7 +214,7 @@ status = "okay"; }; - phy@47401300 { + usb-phy@47401300 { status = "okay"; }; diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index a38f8d362a86..f9c5da9c7fe1 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -354,7 +354,7 @@ status = "disabled"; }; - usb0_phy: phy@47401300 { + usb0_phy: usb-phy@47401300 { compatible = "ti,am335x-usb-phy"; reg = <0x47401300 0x100>; reg-names = "phy"; @@ -364,25 +364,19 @@ usb0: usb@47401000 { compatible = "ti,musb-am33xx"; - ranges; - #address-cells = <1>; - #size-cells = <1>; - reg = <0x47401000 0x200>; - reg-names = "control"; status = "disabled"; - - musb0: usb@47401400 { - compatible = "mg,musbmhdrc"; - reg = <0x47401400 0x400>; - reg-names = "mc"; - interrupts = <18>; - interrupt-names = "mc"; - multipoint = <1>; - num-eps = <16>; - ram-bits = <12>; - port-mode = <3>; - power = <250>; - phys = <&usb0_phy>; + reg = <0x47401400 0x400 + 0x47401000 0x200>; + reg-names = "mc", "control"; + + interrupts = <18>; + interrupt-names = "mc"; + dr_mode = "otg"; + mentor,multipoint = <1>; + mentor,num-eps = <16>; + mentor,ram-bits = <12>; + mentor,power = <500>; + phys = <&usb0_phy>; dmas = <&cppi41dma 0 0 &cppi41dma 1 0 &cppi41dma 2 0 &cppi41dma 3 0 @@ -406,10 +400,9 @@ "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", "tx8", "tx9", "tx10", "tx11", "tx12", "tx13", "tx14", "tx15"; - }; }; - usb1_phy: phy@47401b00 { + usb1_phy: usb-phy@47401b00 { compatible = "ti,am335x-usb-phy"; reg = <0x47401b00 0x100>; reg-names = "phy"; @@ -419,25 +412,18 @@ usb1: usb@47401800 { compatible = "ti,musb-am33xx"; - ranges; - #address-cells = <1>; - #size-cells = <1>; - reg = <0x47401800 0x200>; - reg-names = "control"; status = "disabled"; - - musb1: usb@47401c00 { - compatible = "mg,musbmhdrc"; - reg = <0x47401c00 0x400>; - reg-names = "mc"; - interrupts = <19>; - interrupt-names = "mc"; - multipoint = <1>; - num-eps = <16>; - ram-bits = <12>; - port-mode = <3>; - power = <250>; - phys = <&usb1_phy>; + reg = <0x47401c00 0x400 + 0x47401800 0x200>; + reg-names = "mc", "control"; + interrupts = <19>; + interrupt-names = "mc"; + dr_mode = "otg"; + mentor,multipoint = <1>; + mentor,num-eps = <16>; + mentor,ram-bits = <12>; + mentor,power = <500>; + phys = <&usb1_phy>; dmas = <&cppi41dma 15 0 &cppi41dma 16 0 &cppi41dma 17 0 &cppi41dma 18 0 @@ -461,10 +447,9 @@ "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", "tx8", "tx9", "tx10", "tx11", "tx12", "tx13", "tx14", "tx15"; - }; }; - cppi41dma: dma@07402000 { + cppi41dma: dma-controller@07402000 { compatible = "ti,am3359-cppi41"; reg = <0x47400000 0x1000 0x47402000 0x1000 diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 4ad52e76c4dd..4ead0f5f1f4c 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "musb_core.h" @@ -425,35 +426,51 @@ static int get_int_prop(struct device_node *dn, const char *s) return val; } +static int get_musb_port_mode(struct device *dev) +{ + enum usb_dr_mode mode; + + mode = of_usb_get_dr_mode(dev->of_node); + switch (mode) { + case USB_DR_MODE_HOST: + return MUSB_PORT_MODE_HOST; + + case USB_DR_MODE_PERIPHERAL: + return MUSB_PORT_MODE_GADGET; + + case USB_DR_MODE_UNKNOWN: + case USB_DR_MODE_OTG: + default: + return MUSB_PORT_MODE_DUAL_ROLE; + }; +} + static int dsps_create_musb_pdev(struct dsps_glue *glue, struct platform_device *parent) { struct musb_hdrc_platform_data pdata; struct resource resources[2]; + struct resource *res; struct device *dev = &parent->dev; struct musb_hdrc_config *config; struct platform_device *musb; struct device_node *dn = parent->dev.of_node; - struct device_node *child_node; int ret; - child_node = of_get_child_by_name(dn, "usb"); - if (!child_node) - return -EINVAL; - memset(resources, 0, sizeof(resources)); - ret = of_address_to_resource(child_node, 0, &resources[0]); - if (ret) { + res = platform_get_resource_byname(parent, IORESOURCE_MEM, "mc"); + if (!res) { dev_err(dev, "failed to get memory.\n"); - return ret; + return -EINVAL; } + resources[0] = *res; - ret = of_irq_to_resource(child_node, 0, &resources[1]); - if (ret == 0) { + res = platform_get_resource_byname(parent, IORESOURCE_IRQ, "mc"); + if (!res) { dev_err(dev, "failed to get irq.\n"); - ret = -EINVAL; - return ret; + return -EINVAL; } + resources[1] = *res; /* allocate the child platform device */ musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); @@ -465,7 +482,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, musb->dev.parent = dev; musb->dev.dma_mask = &musb_dmamask; musb->dev.coherent_dma_mask = musb_dmamask; - musb->dev.of_node = of_node_get(child_node); + musb->dev.of_node = of_node_get(dn); glue->musb = musb; @@ -485,11 +502,12 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, pdata.config = config; pdata.platform_ops = &dsps_ops; - config->num_eps = get_int_prop(child_node, "num-eps"); - config->ram_bits = get_int_prop(child_node, "ram-bits"); - pdata.mode = get_int_prop(child_node, "port-mode"); - pdata.power = get_int_prop(child_node, "power"); - config->multipoint = of_property_read_bool(child_node, "multipoint"); + config->num_eps = get_int_prop(dn, "mentor,num-eps"); + config->ram_bits = get_int_prop(dn, "mentor,ram-bits"); + pdata.mode = get_musb_port_mode(dev); + /* DT keeps this entry in mA, musb expects it as per USB spec */ + pdata.power = get_int_prop(dn, "mentor,power") / 2; + config->multipoint = of_property_read_bool(dn, "mentor,multipoint"); ret = platform_device_add_data(musb, &pdata, sizeof(pdata)); if (ret) { -- cgit v1.2.3-58-ga151 From 9dfa36218dadf1821da5823773dd689b4e650e00 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 20 Aug 2013 18:35:48 +0200 Subject: usb: musb: dsps: do not set is_active on the first drvbus interrupt Quite early on init there is an vbus / drvvbus interrupt comming and the dsps code sets is_active to one. As a result we see a lot of |musb_bus_suspend 2459: trying to suspend as a_wait_bcon while active until a device is plugged in with pm_runtime enabled in the kernel. After checking davinci, am35, da8xx I noticed that dsps is actually the only one doing this. So remove it and we won't flooded with mesages and the idle port can be suspended. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 4ead0f5f1f4c..392406da3751 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -314,7 +314,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) jiffies + wrp->poll_seconds * HZ); WARNING("VBUS error workaround (delay coming)\n"); } else if (drvvbus) { - musb->is_active = 1; MUSB_HST_MODE(musb); musb->xceiv->otg->default_a = 1; musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; -- cgit v1.2.3-58-ga151 From 781f17983015dae33324e34d1bb831e715fa04d4 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 20 Aug 2013 18:35:49 +0200 Subject: usb: musb: am335x-evm: Do not remove the session bit HOST-only mode This is what I observe: On the first connect, the musb starts with DEVCTL.Session set. On disconnect, musb_core calls try_idle. That functions removes the Session bit signalizing that the session is over (something that only in OTG is required). A new device, that is plugged, is no longer recognized. I've setup a timer and checked the DEVCTL register and I haven't seen a change in VBus and I saw the B-Device bit set. After setting the IDDIG into A mode and forcing the device to behave like a A device, I didn't see a change. Neither VBUS goes to 0b11 nor does a session start request comes. In the TI-v3.2 kernel they skip to call musb_platform_try_idle() in the OTG_STATE_A_WAIT_BCON state while not in OTG mode. Since the second port hast a standard A plug the patch changes the port to run in host mode only and skips the timer which would remove DEVCTL.Session so we can reconnect to another device later. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am335x-evm.dts | 1 + drivers/usb/musb/musb_dsps.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index 648a67e74d1b..e8ec8756e498 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -192,6 +192,7 @@ usb@47401800 { status = "okay"; + dr_mode = "host"; }; dma-controller@07402000 { diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 392406da3751..4047cbb91bac 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -231,6 +231,8 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) glue->last_timer = jiffies; return; } + if (musb->port_mode == MUSB_PORT_MODE_HOST) + return; if (time_after(glue->last_timer, timeout) && timer_pending(&glue->timer)) { -- cgit v1.2.3-58-ga151 From ce26bd23d4ad180883f53709f9a2220dc771c030 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 23 Aug 2013 16:34:43 -0700 Subject: USB: gadget: audit sysfs attribute permissions Convert all USB gadget sysfs attributes to use the _RO or _RW variants, to make them easier to audit and ensure that the permissions are correct. Note, two are left using the DEVICE_ATTR() macro, as there is no DEVICE_ATTR_WO() in Linus's tree, that will happen after 3.12-rc1 is out, a follow-on patch will be sent then. Reviewed-by: Felipe Balbi Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman -- drivers/usb/gadget/composite.c | 8 +++----- drivers/usb/gadget/dummy_hcd.c | 8 ++++---- drivers/usb/gadget/f_mass_storage.c | 14 ++++++-------- drivers/usb/gadget/net2272.c | 4 ++-- drivers/usb/gadget/net2280.c | 18 +++++++++--------- drivers/usb/gadget/storage_common.c | 25 ++++++++++++------------- drivers/usb/gadget/udc-core.c | 14 +++++++------- 7 files changed, 43 insertions(+), 48 deletions(-) --- drivers/usb/gadget/composite.c | 8 +++----- drivers/usb/gadget/dummy_hcd.c | 8 ++++---- drivers/usb/gadget/f_mass_storage.c | 14 ++++++-------- drivers/usb/gadget/net2272.c | 4 ++-- drivers/usb/gadget/net2280.c | 18 +++++++++--------- drivers/usb/gadget/storage_common.c | 25 ++++++++++++------------- drivers/usb/gadget/udc-core.c | 14 +++++++------- 7 files changed, 43 insertions(+), 48 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 55f4df60f327..d4f0f3305759 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1497,17 +1497,15 @@ void composite_disconnect(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ -static ssize_t composite_show_suspended(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t suspended_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_gadget *gadget = dev_to_usb_gadget(dev); struct usb_composite_dev *cdev = get_gadget_data(gadget); return sprintf(buf, "%d\n", cdev->suspended); } - -static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL); +static DEVICE_ATTR_RO(suspended); static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) { diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index c588e8e486e5..06ecd08fd57a 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -868,7 +868,7 @@ static const struct usb_gadget_ops dummy_ops = { /*-------------------------------------------------------------------------*/ /* "function" sysfs attribute */ -static ssize_t show_function(struct device *dev, struct device_attribute *attr, +static ssize_t function_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dummy *dum = gadget_dev_to_dummy(dev); @@ -877,7 +877,7 @@ static ssize_t show_function(struct device *dev, struct device_attribute *attr, return 0; return scnprintf(buf, PAGE_SIZE, "%s\n", dum->driver->function); } -static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); +static DEVICE_ATTR_RO(function); /*-------------------------------------------------------------------------*/ @@ -2290,7 +2290,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) urb->actual_length, urb->transfer_buffer_length); } -static ssize_t show_urbs(struct device *dev, struct device_attribute *attr, +static ssize_t urbs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -2311,7 +2311,7 @@ static ssize_t show_urbs(struct device *dev, struct device_attribute *attr, return size; } -static DEVICE_ATTR(urbs, S_IRUGO, show_urbs, NULL); +static DEVICE_ATTR_RO(urbs); static int dummy_start_ss(struct dummy_hcd *dum_hcd) { diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 4d4e96a5d2e9..313b835eedfd 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2578,14 +2578,12 @@ static int fsg_main_thread(void *common_) /*************************** DEVICE ATTRIBUTES ***************************/ -static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro); -static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua); -static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file); - -static struct device_attribute dev_attr_ro_cdrom = - __ATTR(ro, 0444, fsg_show_ro, NULL); -static struct device_attribute dev_attr_file_nonremovable = - __ATTR(file, 0444, fsg_show_file, NULL); +static DEVICE_ATTR_RW(ro); +static DEVICE_ATTR_RW(nofua); +static DEVICE_ATTR_RW(file); + +static struct device_attribute dev_attr_ro_cdrom = __ATTR_RO(ro); +static struct device_attribute dev_attr_file_nonremovable = __ATTR_RO(file); /****************************** FSG COMMON ******************************/ diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index f1e50a3e322d..bf2bb39f35a2 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -1184,7 +1184,7 @@ static const struct usb_gadget_ops net2272_ops = { /*---------------------------------------------------------------------------*/ static ssize_t -net2272_show_registers(struct device *_dev, struct device_attribute *attr, char *buf) +registers_show(struct device *_dev, struct device_attribute *attr, char *buf) { struct net2272 *dev; char *next; @@ -1308,7 +1308,7 @@ net2272_show_registers(struct device *_dev, struct device_attribute *attr, char return PAGE_SIZE - size; } -static DEVICE_ATTR(registers, S_IRUGO, net2272_show_registers, NULL); +static DEVICE_ATTR_RO(registers); /*---------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index fbd006ab31d3..0781bff70015 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1424,8 +1424,8 @@ static const struct usb_gadget_ops net2280_ops = { */ /* "function" sysfs attribute */ -static ssize_t -show_function (struct device *_dev, struct device_attribute *attr, char *buf) +static ssize_t function_show(struct device *_dev, struct device_attribute *attr, + char *buf) { struct net2280 *dev = dev_get_drvdata (_dev); @@ -1435,10 +1435,10 @@ show_function (struct device *_dev, struct device_attribute *attr, char *buf) return 0; return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); } -static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); +static DEVICE_ATTR_RO(function); -static ssize_t net2280_show_registers(struct device *_dev, - struct device_attribute *attr, char *buf) +static ssize_t registers_show(struct device *_dev, + struct device_attribute *attr, char *buf) { struct net2280 *dev; char *next; @@ -1590,10 +1590,10 @@ static ssize_t net2280_show_registers(struct device *_dev, return PAGE_SIZE - size; } -static DEVICE_ATTR(registers, S_IRUGO, net2280_show_registers, NULL); +static DEVICE_ATTR_RO(registers); -static ssize_t -show_queues (struct device *_dev, struct device_attribute *attr, char *buf) +static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, + char *buf) { struct net2280 *dev; char *next; @@ -1690,7 +1690,7 @@ done: spin_unlock_irqrestore (&dev->lock, flags); return PAGE_SIZE - size; } -static DEVICE_ATTR (queues, S_IRUGO, show_queues, NULL); +static DEVICE_ATTR_RO(queues); #else diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 8c71212a8c52..08a1a3210a21 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -547,8 +547,8 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr) /*-------------------------------------------------------------------------*/ -static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t ro_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct fsg_lun *curlun = fsg_lun_from_dev(dev); @@ -557,16 +557,16 @@ static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr, : curlun->initially_ro); } -static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t nofua_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct fsg_lun *curlun = fsg_lun_from_dev(dev); return sprintf(buf, "%u\n", curlun->nofua); } -static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t file_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct fsg_lun *curlun = fsg_lun_from_dev(dev); struct rw_semaphore *filesem = dev_get_drvdata(dev); @@ -593,8 +593,8 @@ static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, } -static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t ro_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { ssize_t rc; struct fsg_lun *curlun = fsg_lun_from_dev(dev); @@ -623,9 +623,8 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, return rc; } -static ssize_t fsg_store_nofua(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct fsg_lun *curlun = fsg_lun_from_dev(dev); unsigned nofua; @@ -644,8 +643,8 @@ static ssize_t fsg_store_nofua(struct device *dev, return count; } -static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t file_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct fsg_lun *curlun = fsg_lun_from_dev(dev); struct rw_semaphore *filesem = dev_get_drvdata(dev); diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 546bfda3059a..59891b1c48fc 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -470,31 +470,31 @@ static ssize_t usb_udc_softconn_store(struct device *dev, } static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); -static ssize_t usb_gadget_state_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_udc *udc = container_of(dev, struct usb_udc, dev); struct usb_gadget *gadget = udc->gadget; return sprintf(buf, "%s\n", usb_state_string(gadget->state)); } -static DEVICE_ATTR(state, S_IRUGO, usb_gadget_state_show, NULL); +static DEVICE_ATTR_RO(state); #define USB_UDC_SPEED_ATTR(name, param) \ -ssize_t usb_udc_##param##_show(struct device *dev, \ +ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ return snprintf(buf, PAGE_SIZE, "%s\n", \ usb_speed_string(udc->gadget->param)); \ } \ -static DEVICE_ATTR(name, S_IRUGO, usb_udc_##param##_show, NULL) +static DEVICE_ATTR_RO(name) static USB_UDC_SPEED_ATTR(current_speed, speed); static USB_UDC_SPEED_ATTR(maximum_speed, max_speed); #define USB_UDC_ATTR(name) \ -ssize_t usb_udc_##name##_show(struct device *dev, \ +ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ @@ -502,7 +502,7 @@ ssize_t usb_udc_##name##_show(struct device *dev, \ \ return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ } \ -static DEVICE_ATTR(name, S_IRUGO, usb_udc_##name##_show, NULL) +static DEVICE_ATTR_RO(name) static USB_UDC_ATTR(is_otg); static USB_UDC_ATTR(is_a_peripheral); -- cgit v1.2.3-58-ga151 From 9473e8cfc7570857dfe872b75f8e535ebac4cf53 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Wed, 21 Aug 2013 17:43:31 +0200 Subject: usb: phy: fix build breakage Commit 94ae9843 (usb: phy: rename all phy drivers to phy-$name-usb.c) renamed drivers/usb/phy/otg_fsm.h to drivers/usb/phy/phy-fsm-usb.h but changed drivers/usb/phy/phy-fsm-usb.c to include not existing "phy-otg-fsm.h" instead of new "phy-fsm-usb.h". This breaks building: ... drivers/usb/phy/phy-fsm-usb.c:32:25: fatal error: phy-otg-fsm.h: No such file or directory compilation terminated. make[3]: *** [drivers/usb/phy/phy-fsm-usb.o] Error 1 This commit also missed to modify drivers/usb/phy/phy-fsl-usb.h to include new "phy-fsm-usb.h" instead of "otg_fsm.h" resulting in another build breakage: ... In file included from drivers/usb/phy/phy-fsl-usb.c:46:0: drivers/usb/phy/phy-fsl-usb.h:18:21: fatal error: otg_fsm.h: No such file or directory compilation terminated. make[3]: *** [drivers/usb/phy/phy-fsl-usb.o] Error 1 Fix both issues. Signed-off-by: Anatolij Gustschin Cc: stable@vger.kernel.org Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsl-usb.h | 2 +- drivers/usb/phy/phy-fsm-usb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index ca266280895d..e1859b8ef567 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -15,7 +15,7 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "otg_fsm.h" +#include "phy-fsm-usb.h" #include #include diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index c520b3548e7c..7f4596606e18 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -29,7 +29,7 @@ #include #include -#include "phy-otg-fsm.h" +#include "phy-fsm-usb.h" /* Change USB protocol when there is a protocol change */ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) -- cgit v1.2.3-58-ga151 From c078a37652477e1b08318df8fe403e02cac5138d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 22 Aug 2013 13:46:58 +0530 Subject: usb: dwc3: Remove duplicate inclusion of otg.h otg.h header file was included twice. Signed-off-by: Sachin Kamat Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 577af1b237f2..474162e9d01d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -33,7 +33,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3-58-ga151 From 1826e9b1bd9139850954acb9c2e0fb230ba94e0d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Aug 2013 11:14:49 +0300 Subject: usb: gadget: gadgetfs: use after free in dev_release() The call to put_dev() releases "dev". Hopefully, we don't need to set the state to STATE_DEV_DISABLED anyway so I have removed those lines. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/inode.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 570c005062ab..465ef8e2cc91 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -1270,10 +1270,6 @@ dev_release (struct inode *inode, struct file *fd) dev->buf = NULL; put_dev (dev); - /* other endpoints were all decoupled from this device */ - spin_lock_irq(&dev->lock); - dev->state = STATE_DEV_DISABLED; - spin_unlock_irq(&dev->lock); return 0; } -- cgit v1.2.3-58-ga151 From df4989954abc5ae160865bec79b0f099086decce Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Aug 2013 11:16:15 +0300 Subject: usb: gadget: gadgetfs: potential use after free in unbind() ffs_data_put() can sometimes free "ffs" so I have moved the call down a line below the dereference. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index f394f295d63d..1a66c5baa0d1 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1417,8 +1417,8 @@ static void functionfs_unbind(struct ffs_data *ffs) usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req); ffs->ep0req = NULL; ffs->gadget = NULL; - ffs_data_put(ffs); clear_bit(FFS_FL_BOUND, &ffs->flags); + ffs_data_put(ffs); } } -- cgit v1.2.3-58-ga151 From 0f82768406914d1391ec6d087500db7405f8f68e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 21 Aug 2013 11:41:22 +0300 Subject: usb: phy: signedness bugs in suspend/resume functions "ret" needs to be signed for the error handling to work. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-omap-usb2.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-omap-usb2.c b/drivers/usb/phy/phy-omap-usb2.c index 844ab68f08d0..d266861d24f7 100644 --- a/drivers/usb/phy/phy-omap-usb2.c +++ b/drivers/usb/phy/phy-omap-usb2.c @@ -98,8 +98,8 @@ static int omap_usb_set_peripheral(struct usb_otg *otg, static int omap_usb2_suspend(struct usb_phy *x, int suspend) { - u32 ret; struct omap_usb *phy = phy_to_omapusb(x); + int ret; if (suspend && !phy->is_suspended) { omap_control_usb_phy_power(phy->control_dev, 0); @@ -108,8 +108,7 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend) } else if (!suspend && phy->is_suspended) { ret = pm_runtime_get_sync(phy->dev); if (ret < 0) { - dev_err(phy->dev, "get_sync failed with err %d\n", - ret); + dev_err(phy->dev, "get_sync failed with err %d\n", ret); return ret; } omap_control_usb_phy_power(phy->control_dev, 1); @@ -209,9 +208,9 @@ static int omap_usb2_runtime_suspend(struct device *dev) static int omap_usb2_runtime_resume(struct device *dev) { - u32 ret = 0; struct platform_device *pdev = to_platform_device(dev); struct omap_usb *phy = platform_get_drvdata(pdev); + int ret; ret = clk_enable(phy->wkupclk); if (ret < 0) { -- cgit v1.2.3-58-ga151 From ffb62a14c7b6109ca4ee9bb360ad867b294acddc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 21 Aug 2013 11:42:24 +0300 Subject: usb: gadget: double unlocks on error in atmel_usba_start() The "goto out" statements were wrong. We aren't holding any locks at that point so we should return directly. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/atmel_usba_udc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 40d23384b716..2cb52e0438df 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1772,7 +1772,7 @@ out: static int atmel_usba_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - int ret = 0; + int ret; struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); unsigned long flags; @@ -1784,11 +1784,11 @@ static int atmel_usba_start(struct usb_gadget *gadget, ret = clk_prepare_enable(udc->pclk); if (ret) - goto out; + return ret; ret = clk_prepare_enable(udc->hclk); if (ret) { clk_disable_unprepare(udc->pclk); - goto out; + return ret; } DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name); @@ -1804,11 +1804,9 @@ static int atmel_usba_start(struct usb_gadget *gadget, usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); } - -out: spin_unlock_irqrestore(&udc->lock, flags); - return ret; + return 0; } static int atmel_usba_stop(struct usb_gadget *gadget, -- cgit v1.2.3-58-ga151 From 0f2aa8caeaa043f6cbe6281eb72efba5ff860904 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 21 Aug 2013 12:47:03 +0100 Subject: usb: musb: ux500: Add check for NULL board data Dan Carpenter's automatic Smatch checker found an anomaly in the ux500 MUSB driver, whereby board data was checked before use in all but one occasion. It is believed that it needs to be checked every time. Smatch complaint: drivers/usb/musb/ux500_dma.c:335 ux500_dma_controller_start() error: we previously assumed 'data' could be null (see line 313) Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Cc: Dan Carpenter Signed-off-by: Lee Jones Signed-off-by: Felipe Balbi --- drivers/usb/musb/ux500_dma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index e51dd9b88e71..3700e9713258 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -333,7 +333,9 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller) if (!ux500_channel->dma_chan) ux500_channel->dma_chan = dma_request_channel(mask, - data->dma_filter, + data ? + data->dma_filter : + NULL, param_array[ch_num]); if (!ux500_channel->dma_chan) { -- cgit v1.2.3-58-ga151 From f2e0ae93cc81cb56ed32722da7b7246a24940f90 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Aug 2013 13:13:07 -0700 Subject: USB storage: audit sysfs attribute permissions Convert the usbsorage sysfs attribute to use the _RW macro to make it easier to determine the permissions for the file. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 92b05d95ec5e..94d75edef77f 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -496,7 +496,7 @@ US_DO_ALL_FLAGS ***********************************************************************/ /* Output routine for the sysfs max_sectors file */ -static ssize_t show_max_sectors(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t max_sectors_show(struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_device *sdev = to_scsi_device(dev); @@ -504,7 +504,7 @@ static ssize_t show_max_sectors(struct device *dev, struct device_attribute *att } /* Input routine for the sysfs max_sectors file */ -static ssize_t store_max_sectors(struct device *dev, struct device_attribute *attr, const char *buf, +static ssize_t max_sectors_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct scsi_device *sdev = to_scsi_device(dev); @@ -514,16 +514,14 @@ static ssize_t store_max_sectors(struct device *dev, struct device_attribute *at blk_queue_max_hw_sectors(sdev->request_queue, ms); return count; } - return -EINVAL; + return -EINVAL; } - -static DEVICE_ATTR(max_sectors, S_IRUGO | S_IWUSR, show_max_sectors, - store_max_sectors); +static DEVICE_ATTR_RW(max_sectors); static struct device_attribute *sysfs_device_attr_list[] = { - &dev_attr_max_sectors, - NULL, - }; + &dev_attr_max_sectors, + NULL, +}; /* * this defines our host template, with which we'll allocate hosts -- cgit v1.2.3-58-ga151 From 3cb40a590e338413814234af9badb0b20ba77875 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Tue, 27 Aug 2013 16:10:17 +0800 Subject: usb: fotg210-udc: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Libo Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fotg210-udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/fotg210-udc.c index 10cd18ddd0d4..32db2eee2d87 100644 --- a/drivers/usb/gadget/fotg210-udc.c +++ b/drivers/usb/gadget/fotg210-udc.c @@ -1076,7 +1076,7 @@ static struct usb_gadget_ops fotg210_gadget_ops = { static int fotg210_udc_remove(struct platform_device *pdev) { - struct fotg210_udc *fotg210 = dev_get_drvdata(&pdev->dev); + struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); usb_del_gadget_udc(&fotg210->gadget); iounmap(fotg210->reg); @@ -1134,7 +1134,7 @@ static int fotg210_udc_probe(struct platform_device *pdev) spin_lock_init(&fotg210->lock); - dev_set_drvdata(&pdev->dev, fotg210); + platform_set_drvdata(pdev, fotg210); fotg210->gadget.ops = &fotg210_gadget_ops; -- cgit v1.2.3-58-ga151 From c9a0552e8df596b7cc43cbcd161c065c0046744d Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Tue, 27 Aug 2013 16:10:31 +0800 Subject: usb: renesas_usbhs: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Libo Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 3b39757c13bc..17267b0a2e95 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -499,7 +499,7 @@ static int usbhs_probe(struct platform_device *pdev) goto probe_end_fifo_exit; /* dev_set_drvdata should be called after usbhs_mod_init */ - dev_set_drvdata(&pdev->dev, priv); + platform_set_drvdata(pdev, priv); /* * deviece reset here because -- cgit v1.2.3-58-ga151 From d143a8edcd4e84498e419a33a2af56b3e3a17b9a Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Tue, 27 Aug 2013 11:08:11 +0800 Subject: usb: ohci-at91: remove unnecessary dev_set_drvdata() Unnecessary dev_set_drvdata() is removed, because the driver core clears the driver data to NULL after device_release or on probe failure. Signed-off-by: Libo Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index b3cdd1467521..caa3764a3407 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -235,8 +235,6 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd, clk_put(fclk); clk_put(iclk); fclk = iclk = hclk = NULL; - - dev_set_drvdata(&pdev->dev, NULL); } /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-58-ga151 From 69820e01aa756b8d228143d997f71523c1e97984 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 26 Aug 2013 15:01:40 -0400 Subject: USB: OHCI: Allow runtime PM without system sleep Since ohci-hcd supports runtime PM, the .pm field in its pci_driver structure should be protected by CONFIG_PM rather than CONFIG_PM_SLEEP. Without this change, OHCI controllers won't do runtime suspend if system suspend or hibernation isn't enabled. Signed-off-by: Alan Stern cc: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 08613e241894..767a5eeff848 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -289,7 +289,7 @@ static struct pci_driver ohci_pci_driver = { .remove = usb_hcd_pci_remove, .shutdown = usb_hcd_pci_shutdown, -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM .driver = { .pm = &usb_hcd_pci_pm_ops }, -- cgit v1.2.3-58-ga151 From e7ecf069d4730d5ec4cc91a531eb85c9604142bd Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 28 Aug 2013 09:31:04 -0700 Subject: xhci: Fix warning introduced by disabling runtime PM. The 0day build server caught a new build warning that is triggered when CONFIG_USB_DEFAULT_PERSIST is turned on: tree: git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci.git for-usb-next head: 0730d52a86919300a39a2be37f6c140997dfb82f commit: c8476fb855434c733099079063990e5bfa7ecad6 [1/3] usb: xhci: Disable runtime PM suspend for quirky controllers config: i386-randconfig-r6-0826 (attached as .config) All warnings: drivers/usb/host/xhci.c: In function 'xhci_free_dev': >> drivers/usb/host/xhci.c:3560:17: warning: unused variable 'dev' [-Wunused-variable] struct device *dev = hcd->self.controller; ^ drivers/usb/host/xhci.c: In function 'xhci_alloc_dev': >> drivers/usb/host/xhci.c:3648:17: warning: unused variable 'dev' [-Wunused-variable] struct device *dev = hcd->self.controller; ^ vim +/dev +3560 drivers/usb/host/xhci.c 3554 * disabled. Free any HC data structures associated with that device. 3555 */ 3556 void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) 3557 { 3558 struct xhci_hcd *xhci = hcd_to_xhci(hcd); 3559 struct xhci_virt_device *virt_dev; > 3560 struct device *dev = hcd->self.controller; 3561 unsigned long flags; 3562 u32 state; 3563 int i, ret; 3564 3565 #ifndef CONFIG_USB_DEFAULT_PERSIST 3566 /* 3567 * We called pm_runtime_get_noresume when the device was attached. 3568 * Decrement the counter here to allow controller to runtime suspend 3569 * if no devices remain. 3570 */ 3571 if (xhci->quirks & XHCI_RESET_ON_RESUME) 3572 pm_runtime_put_noidle(dev); 3573 #endif 3574 ... 3641 /* 3642 * Returns 0 if the xHC ran out of device slots, the Enable Slot command 3643 * timed out, or allocating memory failed. Returns 1 on success. 3644 */ 3645 int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) 3646 { 3647 struct xhci_hcd *xhci = hcd_to_xhci(hcd); > 3648 struct device *dev = hcd->self.controller; 3649 unsigned long flags; 3650 int timeleft; 3651 int ret; Fix this. Signed-off-by: Sarah Sharp Cc: Shawn Nematbakhsh --- drivers/usb/host/xhci.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 349d3460a17a..49b6edb84a79 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3557,7 +3557,6 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_virt_device *virt_dev; - struct device *dev = hcd->self.controller; unsigned long flags; u32 state; int i, ret; @@ -3569,7 +3568,7 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) * if no devices remain. */ if (xhci->quirks & XHCI_RESET_ON_RESUME) - pm_runtime_put_noidle(dev); + pm_runtime_put_noidle(hcd->self.controller); #endif ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); @@ -3645,7 +3644,6 @@ static int xhci_reserve_host_control_ep_resources(struct xhci_hcd *xhci) int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct device *dev = hcd->self.controller; unsigned long flags; int timeleft; int ret; @@ -3705,7 +3703,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) * suspend if there is a device attached. */ if (xhci->quirks & XHCI_RESET_ON_RESUME) - pm_runtime_get_noresume(dev); + pm_runtime_get_noresume(hcd->self.controller); #endif /* Is this a LS or FS device under a HS hub? */ -- cgit v1.2.3-58-ga151 From d257221854f0b34cca3247e6c45344d0470f7398 Mon Sep 17 00:00:00 2001 From: Graham Williams Date: Wed, 28 Aug 2013 16:36:14 -0700 Subject: usb: acm gadget: Null termintate strings table The gadget strings table should be null terminated. usb_gadget_get_string() loops through the table expecting a null at the end of the list. Signed-off-by: Graham Williams Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_acm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 4b7e33e5d9c6..ab1065afbbd0 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -285,6 +285,7 @@ static struct usb_string acm_string_defs[] = { [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", [ACM_DATA_IDX].s = "CDC ACM Data", [ACM_IAD_IDX ].s = "CDC Serial", + { } /* end of list */ }; static struct usb_gadget_strings acm_string_table = { -- cgit v1.2.3-58-ga151 From 72031b524cbf610aff53e4fa7b86b52c3238d912 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Fri, 30 Aug 2013 11:23:32 +0800 Subject: usb: phy-tegra-usb: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &of->dev, so we can directly pass a struct platform_device. Signed-off-by: Libo Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 3bfb3d1957c1..e9cb1cb8abc7 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1064,7 +1064,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->u_phy.shutdown = tegra_usb_phy_close; tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend; - dev_set_drvdata(&pdev->dev, tegra_phy); + platform_set_drvdata(pdev, tegra_phy); err = usb_add_phy_dev(&tegra_phy->u_phy); if (err < 0) { -- cgit v1.2.3-58-ga151 From 9b1b0162e9aed89e2ac0a9f01157a6046b896337 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Fri, 30 Aug 2013 11:23:15 +0800 Subject: usb: r8a66597-hcd: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &of->dev, so we can directly pass a struct platform_device. Signed-off-by: Libo Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index a9eef6822a24..2ad004ae747c 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2393,7 +2393,7 @@ static const struct dev_pm_ops r8a66597_dev_pm_ops = { static int r8a66597_remove(struct platform_device *pdev) { - struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev); + struct r8a66597 *r8a66597 = platform_get_drvdata(pdev); struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597); del_timer_sync(&r8a66597->rh_timer); @@ -2466,7 +2466,7 @@ static int r8a66597_probe(struct platform_device *pdev) } r8a66597 = hcd_to_r8a66597(hcd); memset(r8a66597, 0, sizeof(struct r8a66597)); - dev_set_drvdata(&pdev->dev, r8a66597); + platform_set_drvdata(pdev, r8a66597); r8a66597->pdata = dev_get_platdata(&pdev->dev); r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; -- cgit v1.2.3-58-ga151 From fea26ef095cc74f1e11c046a7bf4a29160f8ffb3 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Thu, 29 Aug 2013 11:45:10 +0300 Subject: ehci: remove ehci_vdbg() verbose debugging statements This patch removes ehci_vdbg debugging statements from EHCI host controller driver because they produce too much information, lowering the signal to noise ratio when debugging, and because they are not used anymore. Signed-off-by: Xenia Ragiadakou Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 6 ----- drivers/usb/host/ehci-q.c | 7 ------ drivers/usb/host/ehci-sched.c | 56 ++++++------------------------------------- drivers/usb/host/ehci.h | 5 ---- 4 files changed, 7 insertions(+), 67 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 3bf9f482710c..835fc0844a66 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -211,8 +211,6 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, else t2 |= PORT_WKOC_E | PORT_WKCONN_E; } - ehci_vdbg(ehci, "port %d, %08x -> %08x\n", - port + 1, t1, t2); ehci_writel(ehci, t2, reg); } @@ -302,8 +300,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } if (t1 != t2) { - ehci_vdbg (ehci, "port %d, %08x -> %08x\n", - port + 1, t1, t2); ehci_writel(ehci, t2, reg); changed = 1; } @@ -483,7 +479,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd) if (test_bit(i, &resume_needed)) { temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME); ehci_writel(ehci, temp, &ehci->regs->port_status [i]); - ehci_vdbg (ehci, "resumed port %d\n", i + 1); } } @@ -1204,7 +1199,6 @@ static int ehci_hub_control ( wIndex + 1); temp |= PORT_OWNER; } else { - ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 33336874c47b..cf9f2fb78ff2 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -240,13 +240,6 @@ static int qtd_copy_status ( } else { /* unknown */ status = -EPROTO; } - - ehci_vdbg (ehci, - "dev%d ep%d%s qtd token %08x --> status %d\n", - usb_pipedevice (urb->pipe), - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out", - token, status); } return status; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 66310894ad97..833c35caf501 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -327,17 +327,8 @@ static int tt_available ( periodic_tt_usecs (ehci, dev, frame, tt_usecs); - ehci_vdbg(ehci, "tt frame %d check %d usecs start uframe %d in" - " schedule %d/%d/%d/%d/%d/%d/%d/%d\n", - frame, usecs, uframe, - tt_usecs[0], tt_usecs[1], tt_usecs[2], tt_usecs[3], - tt_usecs[4], tt_usecs[5], tt_usecs[6], tt_usecs[7]); - - if (max_tt_usecs[uframe] <= tt_usecs[uframe]) { - ehci_vdbg(ehci, "frame %d uframe %d fully scheduled\n", - frame, uframe); + if (max_tt_usecs[uframe] <= tt_usecs[uframe]) return 0; - } /* special case for isoc transfers larger than 125us: * the first and each subsequent fully used uframe @@ -348,13 +339,8 @@ static int tt_available ( int ufs = (usecs / 125); int i; for (i = uframe; i < (uframe + ufs) && i < 8; i++) - if (0 < tt_usecs[i]) { - ehci_vdbg(ehci, - "multi-uframe xfer can't fit " - "in frame %d uframe %d\n", - frame, i); + if (0 < tt_usecs[i]) return 0; - } } tt_usecs[uframe] += usecs; @@ -362,12 +348,8 @@ static int tt_available ( carryover_tt_bandwidth(tt_usecs); /* fail if the carryover pushed bw past the last uframe's limit */ - if (max_tt_usecs[7] < tt_usecs[7]) { - ehci_vdbg(ehci, - "tt unavailable usecs %d frame %d uframe %d\n", - usecs, frame, uframe); + if (max_tt_usecs[7] < tt_usecs[7]) return 0; - } } return 1; @@ -1616,16 +1598,9 @@ static void itd_link_urb( next_uframe = stream->next_uframe & (mod - 1); - if (unlikely (list_empty(&stream->td_list))) { + if (unlikely (list_empty(&stream->td_list))) ehci_to_hcd(ehci)->self.bandwidth_allocated += stream->bandwidth; - ehci_vdbg (ehci, - "schedule devp %s ep%d%s-iso period %d start %d.%d\n", - urb->dev->devpath, stream->bEndpointAddress & 0x0f, - (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", - urb->interval, - next_uframe >> 3, next_uframe & 0x7); - } if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci->amd_pll_fix == 1) @@ -1760,14 +1735,9 @@ static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd) usb_amd_quirk_pll_enable(); } - if (unlikely(list_is_singular(&stream->td_list))) { + if (unlikely(list_is_singular(&stream->td_list))) ehci_to_hcd(ehci)->self.bandwidth_allocated -= stream->bandwidth; - ehci_vdbg (ehci, - "deschedule devp %s ep%d%s-iso\n", - dev->devpath, stream->bEndpointAddress & 0x0f, - (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); - } done: itd->urb = NULL; @@ -2025,17 +1995,10 @@ static void sitd_link_urb( next_uframe = stream->next_uframe; - if (list_empty(&stream->td_list)) { + if (list_empty(&stream->td_list)) /* usbfs ignores TT bandwidth */ ehci_to_hcd(ehci)->self.bandwidth_allocated += stream->bandwidth; - ehci_vdbg (ehci, - "sched devp %s ep%d%s-iso [%d] %dms/%04x\n", - urb->dev->devpath, stream->bEndpointAddress & 0x0f, - (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", - (next_uframe >> 3) & (ehci->periodic_size - 1), - stream->interval, hc32_to_cpu(ehci, stream->splits)); - } if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci->amd_pll_fix == 1) @@ -2149,14 +2112,9 @@ static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd) usb_amd_quirk_pll_enable(); } - if (list_is_singular(&stream->td_list)) { + if (list_is_singular(&stream->td_list)) ehci_to_hcd(ehci)->self.bandwidth_allocated -= stream->bandwidth; - ehci_vdbg (ehci, - "deschedule devp %s ep%d%s-iso\n", - dev->devpath, stream->bEndpointAddress & 0x0f, - (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); - } done: sitd->urb = NULL; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 2822e79e1fbc..6067d4bcc037 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -782,11 +782,6 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) #define ehci_warn(ehci, fmt, args...) \ dev_warn(ehci_to_hcd(ehci)->self.controller , fmt , ## args) -#ifdef VERBOSE_DEBUG -# define ehci_vdbg ehci_dbg -#else - static inline void ehci_vdbg(struct ehci_hcd *ehci, ...) {} -#endif #ifndef DEBUG #define STUB_DEBUG_FILES -- cgit v1.2.3-58-ga151 From 1512c91f1c7633b031f69d43f1d3523b29e77593 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Thu, 29 Aug 2013 11:45:13 +0300 Subject: ehci: enable debugging code when CONFIG_DYNAMIC_DEBUG is set The debugging code for ehci is enabled to run if the DEBUG flag is defined. This patch enables the debugging code also when the kernel is configured with dynamic debugging on. Signed-off-by: Xenia Ragiadakou Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 8 ++++---- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/ehci-hcd.c | 6 +++--- drivers/usb/host/ehci-q.c | 4 ++-- drivers/usb/host/ehci-sched.c | 2 +- drivers/usb/host/ehci.h | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 5429d2645bbc..7ad478a80032 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -18,7 +18,7 @@ /* this file is part of ehci-hcd.c */ -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) /* check the values in the HCSPARAMS register * (host controller _Structural_ parameters) @@ -62,7 +62,7 @@ static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {} #endif -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) /* check the values in the HCCPARAMS register * (host controller _Capability_ parameters) @@ -101,7 +101,7 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {} #endif -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) static void __maybe_unused dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd) @@ -301,7 +301,7 @@ static inline int __maybe_unused dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) { return 0; } -#endif /* DEBUG */ +#endif /* DEBUG || CONFIG_DYNAMIC_DEBUG */ /* functions have the "wrong" filename when they're output... */ #define dbg_status(ehci, label, status) { \ diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index e44f442e2fb7..947b009009f1 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -418,7 +418,7 @@ static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) struct fsl_usb2_platform_data *pdata = dev_get_platdata(dev); u32 tmp; -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE); mode &= USBMODE_CM_MASK; tmp = ehci_readl(ehci, hcd->regs + 0x140); /* usbcmd */ diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 73c72997cd4f..103d0767b673 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1300,7 +1300,7 @@ static int __init ehci_hcd_init(void) sizeof(struct ehci_qh), sizeof(struct ehci_qtd), sizeof(struct ehci_itd), sizeof(struct ehci_sitd)); -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) ehci_debug_root = debugfs_create_dir("ehci", usb_debug_root); if (!ehci_debug_root) { retval = -ENOENT; @@ -1349,7 +1349,7 @@ clean2: platform_driver_unregister(&PLATFORM_DRIVER); clean0: #endif -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) debugfs_remove(ehci_debug_root); ehci_debug_root = NULL; err_debug: @@ -1373,7 +1373,7 @@ static void __exit ehci_hcd_cleanup(void) #ifdef PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) debugfs_remove(ehci_debug_root); #endif clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index cf9f2fb78ff2..e321804c3475 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -168,13 +168,13 @@ static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh, * Note: this routine is never called for Isochronous transfers. */ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) struct usb_device *tt = urb->dev->tt->hub; dev_dbg(&tt->dev, "clear tt buffer port %d, a%d ep%d t%08x\n", urb->dev->ttport, urb->dev->devnum, usb_pipeendpoint(urb->pipe), token); -#endif /* DEBUG */ +#endif /* DEBUG || CONFIG_DYNAMIC_DEBUG */ if (!ehci_is_TDI(ehci) || urb->dev->tt->hub != ehci_to_hcd(ehci)->self.root_hub) { diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 833c35caf501..85dd24ed97a6 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -169,7 +169,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) break; } } -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) if (usecs > ehci->uframe_periodic_max) ehci_err (ehci, "uframe %d sched overrun: %d usecs\n", frame * 8 + uframe, usecs); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 6067d4bcc037..291db7d09f22 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -38,7 +38,7 @@ typedef __u16 __bitwise __hc16; #endif /* statistics can be kept for tuning/monitoring */ -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) #define EHCI_STATS #endif @@ -226,7 +226,7 @@ struct ehci_hcd { /* one per controller */ #endif /* debug files */ -#ifdef DEBUG +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) struct dentry *debug_dir; #endif @@ -783,9 +783,9 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) dev_warn(ehci_to_hcd(ehci)->self.controller , fmt , ## args) -#ifndef DEBUG +#if !defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG) #define STUB_DEBUG_FILES -#endif /* DEBUG */ +#endif /* !DEBUG && !CONFIG_DYNAMIC_DEBUG */ /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-58-ga151 From 15532f4761bb50a1ea55f0ab16fa9c4126aa73ca Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Thu, 29 Aug 2013 11:45:12 +0300 Subject: ehci: remove duplicate debug_async_open() prototype in ehci-dbg.c This patch removes the duplicate of debug_async_open() prototype following three lines below the debug_async_open() declaration. Signed-off-by: Xenia Ragiadakou Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 7ad478a80032..aa5b603f3933 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -336,7 +336,6 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { } static int debug_async_open(struct inode *, struct file *); static int debug_periodic_open(struct inode *, struct file *); static int debug_registers_open(struct inode *, struct file *); -static int debug_async_open(struct inode *, struct file *); static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); static int debug_close(struct inode *, struct file *); -- cgit v1.2.3-58-ga151 From eb5ea7300d48c52b028966a6e59f9244faa930d0 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Thu, 29 Aug 2013 11:45:11 +0300 Subject: ehci: remove debugging statement with ehci statistics in ehci_stop() This patch removes the ehci statictics information output in ehci_stop() because they do not provide interesting info. At any case, the current statistics can be viewed by reading the 'registers' file in debugfs. Signed-off-by: Xenia Ragiadakou Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 103d0767b673..5d6022f30ebe 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -440,14 +440,6 @@ static void ehci_stop (struct usb_hcd *hcd) if (ehci->amd_pll_fix == 1) usb_amd_dev_put(); -#ifdef EHCI_STATS - ehci_dbg(ehci, "irq normal %ld err %ld iaa %ld (lost %ld)\n", - ehci->stats.normal, ehci->stats.error, ehci->stats.iaa, - ehci->stats.lost_iaa); - ehci_dbg (ehci, "complete %ld unlink %ld\n", - ehci->stats.complete, ehci->stats.unlink); -#endif - dbg_status (ehci, "ehci_stop completed", ehci_readl(ehci, &ehci->regs->status)); } -- cgit v1.2.3-58-ga151 From e3376d6c87eea09bd65ece6073f6e5d47aa560a3 Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Thu, 29 Aug 2013 21:45:48 +0300 Subject: usbcore: compare and release one bos descriptor in usb_reset_and_verify_device() In usb_reset_and_verify_device(), hub_port_init() allocates a new bos descriptor to hold the value read by the device. The new bos descriptor has to be compared with the old one in order to figure out if device 's firmware has changed in which case the device has to be reenumerated. In the original code, none of the two descriptors was deallocated leading to memory leaks. This patch compares the old bos descriptor with the new one to detect change in firmware and releases the newly allocated bos descriptor to prevent memory leak. Signed-off-by: Xenia Ragiadakou Reported-by: Martin MOKREJS Tested-by: Martin MOKREJS Suggested-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5d859fc07610..3b7ca181cd72 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4944,7 +4944,8 @@ void usb_hub_cleanup(void) } /* usb_hub_cleanup() */ static int descriptors_changed(struct usb_device *udev, - struct usb_device_descriptor *old_device_descriptor) + struct usb_device_descriptor *old_device_descriptor, + struct usb_host_bos *old_bos) { int changed = 0; unsigned index; @@ -4958,6 +4959,16 @@ static int descriptors_changed(struct usb_device *udev, sizeof(*old_device_descriptor)) != 0) return 1; + if ((old_bos && !udev->bos) || (!old_bos && udev->bos)) + return 1; + if (udev->bos) { + len = udev->bos->desc->wTotalLength; + if (len != old_bos->desc->wTotalLength) + return 1; + if (memcmp(udev->bos->desc, old_bos->desc, le16_to_cpu(len))) + return 1; + } + /* Since the idVendor, idProduct, and bcdDevice values in the * device descriptor haven't changed, we will assume the * Manufacturer and Product strings haven't changed either. @@ -5054,6 +5065,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) struct usb_hub *parent_hub; struct usb_hcd *hcd = bus_to_hcd(udev->bus); struct usb_device_descriptor descriptor = udev->descriptor; + struct usb_host_bos *bos; int i, ret = 0; int port1 = udev->portnum; @@ -5071,6 +5083,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } parent_hub = usb_hub_to_struct_hub(parent_hdev); + bos = udev->bos; + udev->bos = NULL; + /* Disable LPM and LTM while we reset the device and reinstall the alt * settings. Device-initiated LPM settings, and system exit latency * settings are cleared when the device is reset, so we have to set @@ -5104,7 +5119,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) goto re_enumerate; /* Device might have changed firmware (DFU or similar) */ - if (descriptors_changed(udev, &descriptor)) { + if (descriptors_changed(udev, &descriptor, bos)) { dev_info(&udev->dev, "device firmware changed\n"); udev->descriptor = descriptor; /* for disconnect() calls */ goto re_enumerate; @@ -5177,11 +5192,15 @@ done: /* Now that the alt settings are re-installed, enable LTM and LPM. */ usb_unlocked_enable_lpm(udev); usb_enable_ltm(udev); + usb_release_bos_descriptor(udev); + udev->bos = bos; return 0; re_enumerate: /* LPM state doesn't matter when we're about to destroy the device. */ hub_port_logical_disconnect(parent_hub, port1); + usb_release_bos_descriptor(udev); + udev->bos = bos; return -ENODEV; } -- cgit v1.2.3-58-ga151 From b9a1048137f4ae43ee90f61a3f34f0efe863cfeb Mon Sep 17 00:00:00 2001 From: Xenia Ragiadakou Date: Sat, 31 Aug 2013 04:40:49 +0300 Subject: usbcore: fix incorrect type in assignment in descriptors_changed() This patch fixes the incorrect assignment of a variable with type 'le16' to a variable with type 'unsigned int'. Signed-off-by: Xenia Ragiadakou Reported-by: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3b7ca181cd72..dde4c83516a1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4962,10 +4962,10 @@ static int descriptors_changed(struct usb_device *udev, if ((old_bos && !udev->bos) || (!old_bos && udev->bos)) return 1; if (udev->bos) { - len = udev->bos->desc->wTotalLength; - if (len != old_bos->desc->wTotalLength) + len = le16_to_cpu(udev->bos->desc->wTotalLength); + if (len != le16_to_cpu(old_bos->desc->wTotalLength)) return 1; - if (memcmp(udev->bos->desc, old_bos->desc, le16_to_cpu(len))) + if (memcmp(udev->bos->desc, old_bos->desc, len)) return 1; } -- cgit v1.2.3-58-ga151