diff options
Diffstat (limited to 'drivers/usb')
108 files changed, 3295 insertions, 1181 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index 3593ce0ec641..880009987460 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -247,7 +247,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev) if (ret) goto err_mux; - ulpi_node = of_find_node_by_name(of_node_get(pdev->dev.of_node), "ulpi"); + ulpi_node = of_get_child_by_name(pdev->dev.of_node, "ulpi"); if (ulpi_node) { phy_node = of_get_next_available_child(ulpi_node, NULL); ci->hsic = of_device_is_compatible(phy_node, "qcom,usb-hsic-phy"); diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 7b65a1040d2c..7f4d2b6af37a 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -29,7 +29,7 @@ static const struct tegra_udc_soc_info tegra20_udc_soc_info = { }; static const struct tegra_udc_soc_info tegra30_udc_soc_info = { - .flags = 0, + .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, }; static const struct tegra_udc_soc_info tegra114_udc_soc_info = { diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 8b351444cc40..9a2ab6751a23 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -180,9 +180,9 @@ static int ulpi_of_register(struct ulpi *ulpi) /* Find a ulpi bus underneath the parent or the grandparent */ parent = ulpi->dev.parent; if (parent->of_node) - np = of_find_node_by_name(parent->of_node, "ulpi"); + np = of_get_child_by_name(parent->of_node, "ulpi"); else if (parent->parent && parent->parent->of_node) - np = of_find_node_by_name(parent->parent->of_node, "ulpi"); + np = of_get_child_by_name(parent->parent->of_node, "ulpi"); if (!np) return 0; diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index da8acd980fc6..c821b4b9647e 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -555,6 +555,9 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, unsigned iad_num = 0; memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); + nintf = nintf_orig = config->desc.bNumInterfaces; + config->desc.bNumInterfaces = 0; // Adjusted later + if (config->desc.bDescriptorType != USB_DT_CONFIG || config->desc.bLength < USB_DT_CONFIG_SIZE || config->desc.bLength > size) { @@ -568,7 +571,6 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, buffer += config->desc.bLength; size -= config->desc.bLength; - nintf = nintf_orig = config->desc.bNumInterfaces; if (nintf > USB_MAXINTERFACES) { dev_warn(ddev, "config %d has too many interfaces: %d, " "using maximum allowed: %d\n", @@ -905,14 +907,25 @@ void usb_release_bos_descriptor(struct usb_device *dev) } } +static const __u8 bos_desc_len[256] = { + [USB_CAP_TYPE_WIRELESS_USB] = USB_DT_USB_WIRELESS_CAP_SIZE, + [USB_CAP_TYPE_EXT] = USB_DT_USB_EXT_CAP_SIZE, + [USB_SS_CAP_TYPE] = USB_DT_USB_SS_CAP_SIZE, + [USB_SSP_CAP_TYPE] = USB_DT_USB_SSP_CAP_SIZE(1), + [CONTAINER_ID_TYPE] = USB_DT_USB_SS_CONTN_ID_SIZE, + [USB_PTM_CAP_TYPE] = USB_DT_USB_PTM_ID_SIZE, +}; + /* Get BOS descriptor set */ int usb_get_bos_descriptor(struct usb_device *dev) { struct device *ddev = &dev->dev; struct usb_bos_descriptor *bos; struct usb_dev_cap_header *cap; + struct usb_ssp_cap_descriptor *ssp_cap; unsigned char *buffer; - int length, total_len, num, i; + int length, total_len, num, i, ssac; + __u8 cap_type; int ret; bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL); @@ -965,7 +978,13 @@ int usb_get_bos_descriptor(struct usb_device *dev) dev->bos->desc->bNumDeviceCaps = i; break; } + cap_type = cap->bDevCapabilityType; length = cap->bLength; + if (bos_desc_len[cap_type] && length < bos_desc_len[cap_type]) { + dev->bos->desc->bNumDeviceCaps = i; + break; + } + total_len -= length; if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) { @@ -973,7 +992,7 @@ int usb_get_bos_descriptor(struct usb_device *dev) continue; } - switch (cap->bDevCapabilityType) { + switch (cap_type) { case USB_CAP_TYPE_WIRELESS_USB: /* Wireless USB cap descriptor is handled by wusb */ break; @@ -986,8 +1005,11 @@ int usb_get_bos_descriptor(struct usb_device *dev) (struct usb_ss_cap_descriptor *)buffer; break; case USB_SSP_CAP_TYPE: - dev->bos->ssp_cap = - (struct usb_ssp_cap_descriptor *)buffer; + ssp_cap = (struct usb_ssp_cap_descriptor *)buffer; + ssac = (le32_to_cpu(ssp_cap->bmAttributes) & + USB_SSP_SUBLINK_SPEED_ATTRIBS); + if (length >= USB_DT_USB_SSP_CAP_SIZE(ssac)) + dev->bos->ssp_cap = ssp_cap; break; case CONTAINER_ID_TYPE: dev->bos->ss_id = diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 705c573d0257..45cf8b335793 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1442,14 +1442,18 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb int number_of_packets = 0; unsigned int stream_id = 0; void *buf; - - if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | - USBDEVFS_URB_SHORT_NOT_OK | + unsigned long mask = USBDEVFS_URB_SHORT_NOT_OK | USBDEVFS_URB_BULK_CONTINUATION | USBDEVFS_URB_NO_FSBR | USBDEVFS_URB_ZERO_PACKET | - USBDEVFS_URB_NO_INTERRUPT)) - return -EINVAL; + USBDEVFS_URB_NO_INTERRUPT; + /* USBDEVFS_URB_ISO_ASAP is a special case */ + if (uurb->type == USBDEVFS_URB_TYPE_ISO) + mask |= USBDEVFS_URB_ISO_ASAP; + + if (uurb->flags & ~mask) + return -EINVAL; + if ((unsigned int)uurb->buffer_length >= USBFS_XFER_MAX) return -EINVAL; if (uurb->buffer_length > 0 && !uurb->buffer) @@ -1677,8 +1681,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb u |= URB_ISO_ASAP; if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in) u |= URB_SHORT_NOT_OK; - if (uurb->flags & USBDEVFS_URB_NO_FSBR) - u |= URB_NO_FSBR; if (uurb->flags & USBDEVFS_URB_ZERO_PACKET) u |= URB_ZERO_PACKET; if (uurb->flags & USBDEVFS_URB_NO_INTERRUPT) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 64262a9a8829..9792cedfc351 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -342,8 +342,8 @@ static int usb_probe_interface(struct device *dev) if (driver->disable_hub_initiated_lpm) { lpm_disable_error = usb_unlocked_disable_lpm(udev); if (lpm_disable_error) { - dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.", - __func__, driver->name); + dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n", + __func__, driver->name); error = lpm_disable_error; goto err; } @@ -537,8 +537,8 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (driver->disable_hub_initiated_lpm) { lpm_disable_error = usb_unlocked_disable_lpm(udev); if (lpm_disable_error) { - dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.", - __func__, driver->name); + dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n", + __func__, driver->name); return -ENOMEM; } } @@ -1070,7 +1070,7 @@ static void usb_rebind_intf(struct usb_interface *intf) if (!intf->dev.power.is_prepared) { intf->needs_binding = 0; rc = device_attach(&intf->dev); - if (rc < 0) + if (rc < 0 && rc != -EPROBE_DEFER) dev_warn(&intf->dev, "rebind failed: %d\n", rc); } } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7ccdd3d4db84..c5c1f6cf3228 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -38,6 +38,9 @@ #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 +#define USB_TP_TRANSMISSION_DELAY 40 /* ns */ +#define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */ + /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ @@ -1049,12 +1052,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) ret = hcd->driver->update_hub_device(hcd, hdev, &hub->tt, GFP_NOIO); if (ret < 0) { - dev_err(hub->intfdev, "Host not " - "accepting hub info " - "update.\n"); - dev_err(hub->intfdev, "LS/FS devices " - "and hubs may not work " - "under this hub\n."); + dev_err(hub->intfdev, + "Host not accepting hub info update\n"); + dev_err(hub->intfdev, + "LS/FS devices and hubs may not work under this hub\n"); } } hub_power_on(hub, true); @@ -1352,6 +1353,20 @@ static int hub_configure(struct usb_hub *hub, goto fail; } + /* + * Accumulate wHubDelay + 40ns for every hub in the tree of devices. + * The resulting value will be used for SetIsochDelay() request. + */ + if (hub_is_superspeed(hdev) || hub_is_superspeedplus(hdev)) { + u32 delay = __le16_to_cpu(hub->descriptor->u.ss.wHubDelay); + + if (hdev->parent) + delay += hdev->parent->hub_delay; + + delay += USB_TP_TRANSMISSION_DELAY; + hdev->hub_delay = min_t(u32, delay, USB_TP_TRANSMISSION_DELAY_MAX); + } + maxchild = hub->descriptor->bNbrPorts; dev_info(hub_dev, "%d port%s detected\n", maxchild, (maxchild == 1) ? "" : "s"); @@ -3157,7 +3172,7 @@ 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, "Failed to disable LTM before suspend\n."); + dev_err(&udev->dev, "Failed to disable LTM before suspend\n"); status = -ENOMEM; if (PMSG_IS_AUTO(msg)) goto err_ltm; @@ -4599,7 +4614,20 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (retval >= 0) retval = -EMSGSIZE; } else { + u32 delay; + retval = 0; + + delay = udev->parent->hub_delay; + udev->hub_delay = min_t(u32, delay, + USB_TP_TRANSMISSION_DELAY_MAX); + retval = usb_set_isoch_delay(udev); + if (retval) { + dev_dbg(&udev->dev, + "Failed set isoch delay, error %d\n", + retval); + retval = 0; + } break; } } @@ -4948,6 +4976,15 @@ loop: usb_put_dev(udev); if ((status == -ENOTCONN) || (status == -ENOTSUPP)) break; + + /* When halfway through our retry count, power-cycle the port */ + if (i == (SET_CONFIG_TRIES / 2) - 1) { + dev_info(&port_dev->dev, "attempt power cycle\n"); + usb_hub_set_port_power(hdev, hub, port1, false); + msleep(2 * hub_power_on_good_delay(hub)); + usb_hub_set_port_power(hdev, hub, port1, true); + msleep(hub_power_on_good_delay(hub)); + } } if (hub->hdev->parent || !hcd->driver->port_handed_over || @@ -5475,13 +5512,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev) */ ret = usb_unlocked_disable_lpm(udev); if (ret) { - dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); + dev_err(&udev->dev, "%s Failed to disable LPM\n", __func__); goto re_enumerate_no_bos; } ret = usb_disable_ltm(udev); if (ret) { - dev_err(&udev->dev, "%s Failed to disable LTM\n.", - __func__); + dev_err(&udev->dev, "%s Failed to disable LTM\n", __func__); goto re_enumerate_no_bos; } diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c index 9dbb429cd471..d775ffea20c3 100644 --- a/drivers/usb/core/ledtrig-usbport.c +++ b/drivers/usb/core/ledtrig-usbport.c @@ -137,11 +137,17 @@ static bool usbport_trig_port_observed(struct usbport_trig_data *usbport_data, if (!led_np) return false; - /* Get node of port being added */ - port_np = usb_of_get_child_node(usb_dev->dev.of_node, port1); + /* + * Get node of port being added + * + * FIXME: This is really the device node of the connected device + */ + port_np = usb_of_get_device_node(usb_dev, port1); if (!port_np) return false; + of_node_put(port_np); + /* Amount of trigger sources for this LED */ count = of_count_phandle_with_args(led_np, "trigger-sources", "#trigger-source-cells"); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 77001bcfc504..c64cf6c4a83d 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -18,6 +18,7 @@ #include <linux/usb/cdc.h> #include <linux/usb/quirks.h> #include <linux/usb/hcd.h> /* for usbcore internals */ +#include <linux/usb/of.h> #include <asm/byteorder.h> #include "usb.h" @@ -776,7 +777,7 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) * deal with strings at all. Set string_langid to -1 in order to * prevent any string to be retrieved from the device */ if (err < 0) { - dev_err(&dev->dev, "string descriptor 0 read error: %d\n", + dev_info(&dev->dev, "string descriptor 0 read error: %d\n", err); dev->string_langid = -1; return -EPIPE; @@ -915,6 +916,30 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) return ret; } +/* + * usb_set_isoch_delay - informs the device of the packet transmit delay + * @dev: the device whose delay is to be informed + * Context: !in_interrupt() + * + * Since this is an optional request, we don't bother if it fails. + */ +int usb_set_isoch_delay(struct usb_device *dev) +{ + /* skip hub devices */ + if (dev->descriptor.bDeviceClass == USB_CLASS_HUB) + return 0; + + /* skip non-SS/non-SSP devices */ + if (dev->speed < USB_SPEED_SUPER) + return 0; + + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_ISOCH_DELAY, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + cpu_to_le16(dev->hub_delay), 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} + /** * usb_get_status - issues a GET_STATUS call * @dev: the device whose status is being checked @@ -1355,7 +1380,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * so that the xHCI driver can recalculate the U1/U2 timeouts. */ if (usb_disable_lpm(dev)) { - dev_err(&iface->dev, "%s Failed to disable LPM\n.", __func__); + dev_err(&iface->dev, "%s Failed to disable LPM\n", __func__); mutex_unlock(hcd->bandwidth_mutex); return -ENOMEM; } @@ -1499,7 +1524,7 @@ int usb_reset_configuration(struct usb_device *dev) * that the xHCI driver can recalculate the U1/U2 timeouts. */ if (usb_disable_lpm(dev)) { - dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__); + dev_err(&dev->dev, "%s Failed to disable LPM\n", __func__); mutex_unlock(hcd->bandwidth_mutex); return -ENOMEM; } @@ -1583,6 +1608,7 @@ static void usb_release_interface(struct device *dev) kref_put(&intfc->ref, usb_release_interface_cache); usb_put_dev(interface_to_usbdev(intf)); + of_node_put(dev->of_node); kfree(intf); } @@ -1846,7 +1872,7 @@ free_interfaces: * timeouts. */ if (dev->actconfig && usb_disable_lpm(dev)) { - dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__); + dev_err(&dev->dev, "%s Failed to disable LPM\n", __func__); mutex_unlock(hcd->bandwidth_mutex); ret = -ENOMEM; goto free_interfaces; @@ -1868,6 +1894,7 @@ free_interfaces: struct usb_interface_cache *intfc; struct usb_interface *intf; struct usb_host_interface *alt; + u8 ifnum; cp->interface[i] = intf = new_interfaces[i]; intfc = cp->intf_cache[i]; @@ -1886,11 +1913,17 @@ free_interfaces: if (!alt) alt = &intf->altsetting[0]; - intf->intf_assoc = - find_iad(dev, cp, alt->desc.bInterfaceNumber); + ifnum = alt->desc.bInterfaceNumber; + intf->intf_assoc = find_iad(dev, cp, ifnum); intf->cur_altsetting = alt; usb_enable_interface(dev, intf, true); intf->dev.parent = &dev->dev; + if (usb_of_has_combined_node(dev)) { + device_set_of_node_from_dev(&intf->dev, &dev->dev); + } else { + intf->dev.of_node = usb_of_get_interface_node(dev, + configuration, ifnum); + } intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; @@ -1905,9 +1938,8 @@ free_interfaces: intf->minor = -1; device_initialize(&intf->dev); pm_runtime_no_callbacks(&intf->dev); - dev_set_name(&intf->dev, "%d-%s:%d.%d", - dev->bus->busnum, dev->devpath, - configuration, alt->desc.bInterfaceNumber); + dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, + dev->devpath, configuration, ifnum); usb_get_dev(dev); } kfree(new_interfaces); diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index 2be968353257..fd77442c2d12 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -3,7 +3,8 @@ * of.c The helpers for hcd device tree support * * Copyright (C) 2016 Freescale Semiconductor, Inc. - * Author: Peter Chen <peter.chen@freescale.com> + * Author: Peter Chen <peter.chen@freescale.com> + * Copyright (C) 2017 Johan Hovold <johan@kernel.org> */ #include <linux/of.h> @@ -11,31 +12,99 @@ #include <linux/usb/of.h> /** - * usb_of_get_child_node - Find the device node match port number - * @parent: the parent device node - * @portnum: the port number which device is connecting + * usb_of_get_device_node() - get a USB device node + * @hub: hub to which device is connected + * @port1: one-based index of port * - * Find the node from device tree according to its port number. + * Look up the node of a USB device given its parent hub device and one-based + * port number. * * Return: A pointer to the node with incremented refcount if found, or * %NULL otherwise. */ -struct device_node *usb_of_get_child_node(struct device_node *parent, - int portnum) +struct device_node *usb_of_get_device_node(struct usb_device *hub, int port1) { struct device_node *node; - u32 port; + u32 reg; - for_each_child_of_node(parent, node) { - if (!of_property_read_u32(node, "reg", &port)) { - if (port == portnum) - return node; + for_each_child_of_node(hub->dev.of_node, node) { + if (of_property_read_u32(node, "reg", ®)) + continue; + + if (reg == port1) + return node; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(usb_of_get_device_node); + +/** + * usb_of_has_combined_node() - determine whether a device has a combined node + * @udev: USB device + * + * Determine whether a USB device has a so called combined node which is + * shared with its sole interface. This is the case if and only if the device + * has a node and its decriptors report the following: + * + * 1) bDeviceClass is 0 or 9, and + * 2) bNumConfigurations is 1, and + * 3) bNumInterfaces is 1. + * + * Return: True iff the device has a device node and its descriptors match the + * criteria for a combined node. + */ +bool usb_of_has_combined_node(struct usb_device *udev) +{ + struct usb_device_descriptor *ddesc = &udev->descriptor; + struct usb_config_descriptor *cdesc; + + if (!udev->dev.of_node) + return false; + + switch (ddesc->bDeviceClass) { + case USB_CLASS_PER_INTERFACE: + case USB_CLASS_HUB: + if (ddesc->bNumConfigurations == 1) { + cdesc = &udev->config->desc; + if (cdesc->bNumInterfaces == 1) + return true; } } + return false; +} +EXPORT_SYMBOL_GPL(usb_of_has_combined_node); + +/** + * usb_of_get_interface_node() - get a USB interface node + * @udev: USB device of interface + * @config: configuration value + * @ifnum: interface number + * + * Look up the node of a USB interface given its USB device, configuration + * value and interface number. + * + * Return: A pointer to the node with incremented refcount if found, or + * %NULL otherwise. + */ +struct device_node * +usb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum) +{ + struct device_node *node; + u32 reg[2]; + + for_each_child_of_node(udev->dev.of_node, node) { + if (of_property_read_u32_array(node, "reg", reg, 2)) + continue; + + if (reg[0] == ifnum && reg[1] == config) + return node; + } + return NULL; } -EXPORT_SYMBOL_GPL(usb_of_get_child_node); +EXPORT_SYMBOL_GPL(usb_of_get_interface_node); /** * usb_of_get_companion_dev - Find the companion device diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index f1dbab6f798f..4024926c1d68 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -52,10 +52,11 @@ static const struct usb_device_id usb_quirk_list[] = { /* Microsoft LifeCam-VX700 v2.0 */ { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME }, - /* Logitech HD Pro Webcams C920, C920-C and C930e */ + /* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */ { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, { USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT }, { USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT }, + { USB_DEVICE(0x046d, 0x085b), .driver_info = USB_QUIRK_DELAY_INIT }, /* Logitech ConferenceCam CC3000e */ { USB_DEVICE(0x046d, 0x0847), .driver_info = USB_QUIRK_DELAY_INIT }, @@ -146,6 +147,12 @@ static const struct usb_device_id usb_quirk_list[] = { /* appletouch */ { USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Genesys Logic hub, internally used by KY-688 USB 3.1 Type-C Hub */ + { USB_DEVICE(0x05e3, 0x0612), .driver_info = USB_QUIRK_NO_LPM }, + + /* ELSA MicroLink 56K */ + { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Genesys Logic hub, internally used by Moshi USB to Ethernet Adapter */ { USB_DEVICE(0x05e3, 0x0616), .driver_info = USB_QUIRK_NO_LPM }, diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 9fdf137c4865..796c9b149728 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -479,9 +479,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (is_out) allowed |= URB_ZERO_PACKET; /* FALLTHROUGH */ - case USB_ENDPOINT_XFER_CONTROL: - allowed |= URB_NO_FSBR; /* only affects UHCI */ - /* FALLTHROUGH */ default: /* all non-iso endpoints */ if (!is_out) allowed |= URB_SHORT_NOT_OK; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 845286f08ab0..2f5fbc56a9dd 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -645,8 +645,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, raw_port = usb_hcd_find_raw_port_number(usb_hcd, port1); } - dev->dev.of_node = usb_of_get_child_node(parent->dev.of_node, - raw_port); + dev->dev.of_node = usb_of_get_device_node(parent, raw_port); /* hub driver sets up TT records */ } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 2bee08d084ae..149cc7480971 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -40,6 +40,7 @@ extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); +extern int usb_set_isoch_delay(struct usb_device *dev); extern int usb_get_bos_descriptor(struct usb_device *dev); extern void usb_release_bos_descriptor(struct usb_device *dev); extern char *usb_cache_string(struct usb_device *udev, int index); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index f66c94130cac..cd77af3b1565 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -537,6 +537,7 @@ struct dwc2_core_params { * 2 - Internal DMA * @power_optimized Are power optimizations enabled? * @num_dev_ep Number of device endpoints available + * @num_dev_in_eps Number of device IN endpoints available * @num_dev_perio_in_ep Number of device periodic IN endpoints * available * @dev_token_q_depth Device Mode IN Token Sequence Learning Queue @@ -565,6 +566,7 @@ struct dwc2_core_params { * 2 - 8 or 16 bits * @snpsid: Value from SNPSID register * @dev_ep_dirs: Direction of device endpoints (GHWCFG1) + * @g_tx_fifo_size[] Power-on values of TxFIFO sizes */ struct dwc2_hw_params { unsigned op_mode:3; @@ -586,12 +588,14 @@ struct dwc2_hw_params { unsigned fs_phy_type:2; unsigned i2c_enable:1; unsigned num_dev_ep:4; + unsigned num_dev_in_eps : 4; unsigned num_dev_perio_in_ep:4; unsigned total_fifo_size:16; unsigned power_optimized:1; unsigned utmi_phy_data_width:2; u32 snpsid; u32 dev_ep_dirs; + u32 g_tx_fifo_size[MAX_EPS_CHANNELS]; }; /* Size of control and EP0 buffers */ @@ -925,6 +929,7 @@ struct dwc2_hsotg { int irq; struct clk *clk; struct reset_control *reset; + struct reset_control *reset_ecc; unsigned int queuing_high_bandwidth:1; unsigned int srp_success:1; @@ -967,6 +972,7 @@ struct dwc2_hsotg { } flags; struct list_head non_periodic_sched_inactive; + struct list_head non_periodic_sched_waiting; struct list_head non_periodic_sched_active; struct list_head *non_periodic_qh_ptr; struct list_head periodic_sched_inactive; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 88529d092503..e4c3ce0de5de 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -195,55 +195,18 @@ int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) { if (hsotg->hw_params.en_multiple_tx_fifo) /* In dedicated FIFO mode we need count of IN EPs */ - return (dwc2_readl(hsotg->regs + GHWCFG4) & - GHWCFG4_NUM_IN_EPS_MASK) >> GHWCFG4_NUM_IN_EPS_SHIFT; + return hsotg->hw_params.num_dev_in_eps; else /* In shared FIFO mode we need count of Periodic IN EPs */ return hsotg->hw_params.num_dev_perio_in_ep; } /** - * dwc2_hsotg_ep_info_size - return Endpoint Info Control block size in DWORDs - */ -static int dwc2_hsotg_ep_info_size(struct dwc2_hsotg *hsotg) -{ - int val = 0; - int i; - u32 ep_dirs; - - /* - * Don't need additional space for ep info control registers in - * slave mode. - */ - if (!using_dma(hsotg)) { - dev_dbg(hsotg->dev, "Buffer DMA ep info size 0\n"); - return 0; - } - - /* - * Buffer DMA mode - 1 location per endpoit - * Descriptor DMA mode - 4 locations per endpoint - */ - ep_dirs = hsotg->hw_params.dev_ep_dirs; - - for (i = 0; i <= hsotg->hw_params.num_dev_ep; i++) { - val += ep_dirs & 3 ? 1 : 2; - ep_dirs >>= 2; - } - - if (using_desc_dma(hsotg)) - val = val * 4; - - return val; -} - -/** * dwc2_hsotg_tx_fifo_total_depth - return total FIFO depth available for * device mode TX FIFOs */ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) { - int ep_info_size; int addr; int tx_addr_max; u32 np_tx_fifo_size; @@ -252,8 +215,7 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) hsotg->params.g_np_tx_fifo_size); /* Get Endpoint Info Control block size in DWORDs. */ - ep_info_size = dwc2_hsotg_ep_info_size(hsotg); - tx_addr_max = hsotg->hw_params.total_fifo_size - ep_info_size; + tx_addr_max = hsotg->hw_params.total_fifo_size; addr = hsotg->params.g_rx_fifo_size + np_tx_fifo_size; if (tx_addr_max <= addr) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 7b6eb0ad513b..a5d72fcd1603 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -659,6 +659,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg, list_for_each_entry(qh, &hsotg->non_periodic_sched_inactive, qh_list_entry) dev_dbg(hsotg->dev, " %p\n", qh); + dev_dbg(hsotg->dev, " NP waiting sched:\n"); + list_for_each_entry(qh, &hsotg->non_periodic_sched_waiting, + qh_list_entry) + dev_dbg(hsotg->dev, " %p\n", qh); dev_dbg(hsotg->dev, " NP active sched:\n"); list_for_each_entry(qh, &hsotg->non_periodic_sched_active, qh_list_entry) @@ -1818,6 +1822,7 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg, static void dwc2_kill_all_urbs(struct dwc2_hsotg *hsotg) { dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_inactive); + dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_waiting); dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_active); dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_inactive); dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_ready); @@ -4998,6 +5003,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) /* Free memory for QH/QTD lists */ dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_inactive); + dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_waiting); dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_active); dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_inactive); dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_ready); @@ -5159,6 +5165,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) /* Initialize the non-periodic schedule */ INIT_LIST_HEAD(&hsotg->non_periodic_sched_inactive); + INIT_LIST_HEAD(&hsotg->non_periodic_sched_waiting); INIT_LIST_HEAD(&hsotg->non_periodic_sched_active); /* Initialize the periodic schedule */ diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 78e9e01051b5..ad60e46e66e1 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -314,12 +314,16 @@ struct dwc2_hs_transfer_time { * descriptor and indicates original XferSize value for the * descriptor * @unreserve_timer: Timer for releasing periodic reservation. + * @wait_timer: Timer used to wait before re-queuing. * @dwc2_tt: Pointer to our tt info (or NULL if no tt). * @ttport: Port number within our tt. * @tt_buffer_dirty True if clear_tt_buffer_complete is pending * @unreserve_pending: True if we planned to unreserve but haven't yet. * @schedule_low_speed: True if we have a low/full speed component (either the * host is in low/full speed mode or do_split). + * @want_wait: We should wait before re-queuing; only matters for non- + * periodic transfers and is ignored for periodic ones. + * @wait_timer_cancel: Set to true to cancel the wait_timer. * * A Queue Head (QH) holds the static characteristics of an endpoint and * maintains a list of transfers (QTDs) for that endpoint. A QH structure may @@ -354,11 +358,14 @@ struct dwc2_qh { u32 desc_list_sz; u32 *n_bytes; struct timer_list unreserve_timer; + struct timer_list wait_timer; struct dwc2_tt *dwc_tt; int ttport; unsigned tt_buffer_dirty:1; unsigned unreserve_pending:1; unsigned schedule_low_speed:1; + unsigned want_wait:1; + unsigned wait_timer_cancel:1; }; /** @@ -389,6 +396,7 @@ struct dwc2_qh { * @n_desc: Number of DMA descriptors for this QTD * @isoc_frame_index_last: Last activated frame (packet) index, used in * descriptor DMA mode only + * @num_naks: Number of NAKs received on this QTD. * @urb: URB for this transfer * @qh: Queue head for this QTD * @qtd_list_entry: For linking to the QH's list of QTDs @@ -419,6 +427,7 @@ struct dwc2_qtd { u8 error_count; u8 n_desc; u16 isoc_frame_index_last; + u16 num_naks; struct dwc2_hcd_urb *urb; struct dwc2_qh *qh; struct list_head qtd_list_entry; diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 916d991b96b8..a5dfd9d8bd9a 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -53,6 +53,12 @@ #include "core.h" #include "hcd.h" +/* + * If we get this many NAKs on a split transaction we'll slow down + * retransmission. A 1 here means delay after the first NAK. + */ +#define DWC2_NAKS_BEFORE_DELAY 3 + /* This function is for debug only */ static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg) { @@ -1201,11 +1207,25 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg, /* * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and * interrupt. Re-start the SSPLIT transfer. + * + * Normally for non-periodic transfers we'll retry right away, but to + * avoid interrupt storms we'll wait before retrying if we've got + * several NAKs. If we didn't do this we'd retry directly from the + * interrupt handler and could end up quickly getting another + * interrupt (another NAK), which we'd retry. + * + * Note that in DMA mode software only gets involved to re-send NAKed + * transfers for split transactions, so we only need to apply this + * delaying logic when handling splits. In non-DMA mode presumably we + * might want a similar delay if someone can demonstrate this problem + * affects that code path too. */ if (chan->do_split) { if (chan->complete_split) qtd->error_count = 0; qtd->complete_split = 0; + qtd->num_naks++; + qtd->qh->want_wait = qtd->num_naks >= DWC2_NAKS_BEFORE_DELAY; dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK); goto handle_nak_done; } diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index fcd1676c7f0b..e34ad5e65350 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -58,6 +58,9 @@ /* Wait this long before releasing periodic reservation */ #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5)) +/* If we get a NAK, wait this long before retrying */ +#define DWC2_RETRY_WAIT_DELAY (msecs_to_jiffies(1)) + /** * dwc2_periodic_channel_available() - Checks that a channel is available for a * periodic transfer @@ -1441,6 +1444,55 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg, } /** + * dwc2_wait_timer_fn() - Timer function to re-queue after waiting + * + * As per the spec, a NAK indicates that "a function is temporarily unable to + * transmit or receive data, but will eventually be able to do so without need + * of host intervention". + * + * That means that when we encounter a NAK we're supposed to retry. + * + * ...but if we retry right away (from the interrupt handler that saw the NAK) + * then we can end up with an interrupt storm (if the other side keeps NAKing + * us) because on slow enough CPUs it could take us longer to get out of the + * interrupt routine than it takes for the device to send another NAK. That + * leads to a constant stream of NAK interrupts and the CPU locks. + * + * ...so instead of retrying right away in the case of a NAK we'll set a timer + * to retry some time later. This function handles that timer and moves the + * qh back to the "inactive" list, then queues transactions. + * + * @t: Pointer to wait_timer in a qh. + */ +static void dwc2_wait_timer_fn(struct timer_list *t) +{ + struct dwc2_qh *qh = from_timer(qh, t, wait_timer); + struct dwc2_hsotg *hsotg = qh->hsotg; + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + + /* + * We'll set wait_timer_cancel to true if we want to cancel this + * operation in dwc2_hcd_qh_unlink(). + */ + if (!qh->wait_timer_cancel) { + enum dwc2_transaction_type tr_type; + + qh->want_wait = false; + + list_move(&qh->qh_list_entry, + &hsotg->non_periodic_sched_inactive); + + tr_type = dwc2_hcd_select_transactions(hsotg); + if (tr_type != DWC2_TRANSACTION_NONE) + dwc2_hcd_queue_transactions(hsotg, tr_type); + } + + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +/** * dwc2_qh_init() - Initializes a QH structure * * @hsotg: The HCD state structure for the DWC OTG controller @@ -1468,6 +1520,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* Initialize QH */ qh->hsotg = hsotg; timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0); + timer_setup(&qh->wait_timer, dwc2_wait_timer_fn, 0); qh->ep_type = ep_type; qh->ep_is_in = ep_is_in; @@ -1628,6 +1681,16 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) dwc2_do_unreserve(hsotg, qh); spin_unlock_irqrestore(&hsotg->lock, flags); } + + /* + * We don't have the lock so we can safely wait until the wait timer + * finishes. Of course, at this point in time we'd better have set + * wait_timer_active to false so if this timer was still pending it + * won't do anything anyway, but we want it to finish before we free + * memory. + */ + del_timer_sync(&qh->wait_timer); + dwc2_host_put_tt_info(hsotg, qh->dwc_tt); if (qh->desc_list) @@ -1663,9 +1726,16 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) qh->start_active_frame = hsotg->frame_number; qh->next_active_frame = qh->start_active_frame; - /* Always start in inactive schedule */ - list_add_tail(&qh->qh_list_entry, - &hsotg->non_periodic_sched_inactive); + if (qh->want_wait) { + list_add_tail(&qh->qh_list_entry, + &hsotg->non_periodic_sched_waiting); + qh->wait_timer_cancel = false; + mod_timer(&qh->wait_timer, + jiffies + DWC2_RETRY_WAIT_DELAY + 1); + } else { + list_add_tail(&qh->qh_list_entry, + &hsotg->non_periodic_sched_inactive); + } return 0; } @@ -1695,6 +1765,9 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) dev_vdbg(hsotg->dev, "%s()\n", __func__); + /* If the wait_timer is pending, this will stop it from acting */ + qh->wait_timer_cancel = true; + if (list_empty(&qh->qh_list_entry)) /* QH is not in a schedule */ return; @@ -1903,7 +1976,7 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, if (dwc2_qh_is_non_per(qh)) { dwc2_hcd_qh_unlink(hsotg, qh); if (!list_empty(&qh->qtd_list)) - /* Add back to inactive non-periodic schedule */ + /* Add back to inactive/waiting non-periodic schedule */ dwc2_hcd_qh_add(hsotg, qh); return; } diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index ef73af6e03a9..03fd20f0b496 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -484,8 +484,7 @@ static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg) } for (fifo = 1; fifo <= fifo_count; fifo++) { - dptxfszn = (dwc2_readl(hsotg->regs + DPTXFSIZN(fifo)) & - FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; + dptxfszn = hsotg->hw_params.g_tx_fifo_size[fifo]; if (hsotg->params.g_tx_fifo_size[fifo] < min || hsotg->params.g_tx_fifo_size[fifo] > dptxfszn) { @@ -609,6 +608,7 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg) struct dwc2_hw_params *hw = &hsotg->hw_params; bool forced; u32 gnptxfsiz; + int fifo, fifo_count; if (hsotg->dr_mode == USB_DR_MODE_HOST) return; @@ -617,6 +617,14 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg) gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); + fifo_count = dwc2_hsotg_tx_fifo_count(hsotg); + + for (fifo = 1; fifo <= fifo_count; fifo++) { + hw->g_tx_fifo_size[fifo] = + (dwc2_readl(hsotg->regs + DPTXFSIZN(fifo)) & + FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; + } + if (forced) dwc2_clear_force_mode(hsotg); @@ -661,14 +669,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) hwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4); grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); - /* - * Host specific hardware parameters. Reading these parameters - * requires the controller to be in host mode. The mode will - * be forced, if necessary, to read these values. - */ - dwc2_get_host_hwparams(hsotg); - dwc2_get_dev_hwparams(hsotg); - /* hwcfg1 */ hw->dev_ep_dirs = hwcfg1; @@ -711,6 +711,8 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN); hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >> GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT; + hw->num_dev_in_eps = (hwcfg4 & GHWCFG4_NUM_IN_EPS_MASK) >> + GHWCFG4_NUM_IN_EPS_SHIFT; hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA); hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ); hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >> @@ -719,6 +721,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) /* fifo sizes */ hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >> GRXFSIZ_DEPTH_SHIFT; + /* + * Host specific hardware parameters. Reading these parameters + * requires the controller to be in host mode. The mode will + * be forced, if necessary, to read these values. + */ + dwc2_get_host_hwparams(hsotg); + dwc2_get_dev_hwparams(hsotg); return 0; } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 3e26550d13dd..4703478f702f 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -221,6 +221,15 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) reset_control_deassert(hsotg->reset); + hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc"); + if (IS_ERR(hsotg->reset_ecc)) { + ret = PTR_ERR(hsotg->reset_ecc); + dev_err(hsotg->dev, "error getting reset control for ecc %d\n", ret); + return ret; + } + + reset_control_deassert(hsotg->reset_ecc); + /* Set default UTMI width */ hsotg->phyif = GUSBCFG_PHYIF16; @@ -319,6 +328,7 @@ static int dwc2_driver_remove(struct platform_device *dev) dwc2_lowlevel_hw_disable(hsotg); reset_control_assert(hsotg->reset); + reset_control_assert(hsotg->reset_ecc); return 0; } diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 07832509584f..ade2ab00d37a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -147,6 +147,7 @@ static void __dwc3_set_mode(struct work_struct *work) otg_set_vbus(dwc->usb2_phy->otg, true); phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); + phy_calibrate(dwc->usb2_generic_phy); } break; case DWC3_GCTL_PRTCAP_DEVICE: @@ -945,6 +946,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dev_err(dev, "failed to initialize host\n"); return ret; } + phy_calibrate(dwc->usb2_generic_phy); break; case USB_DR_MODE_OTG: INIT_WORK(&dwc->drd_work, __dwc3_set_mode); @@ -1062,6 +1064,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); + dwc->dis_metastability_quirk = device_property_read_bool(dev, + "snps,dis_metastability_quirk"); + dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4a4a4c98508c..03c7aaaac926 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -796,7 +796,6 @@ struct dwc3_scratchpad_array { * @usb2_generic_phy: pointer to USB2 PHY * @usb3_generic_phy: pointer to USB3 PHY * @ulpi: pointer to ulpi interface - * @isoch_delay: wValue from Set Isochronous Delay request; * @u2sel: parameter from Set SEL request. * @u2pel: parameter from Set SEL request. * @u1sel: parameter from Set SEL request. @@ -857,6 +856,7 @@ struct dwc3_scratchpad_array { * 1 - -3.5dB de-emphasis * 2 - No de-emphasis * 3 - Reserved + * @dis_metastability_quirk: set to disable metastability quirk. * @imod_interval: set the interrupt moderation interval in 250ns * increments or 0 to disable. */ @@ -955,7 +955,6 @@ struct dwc3 { enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; - u16 isoch_delay; u16 u2sel; u16 u2pel; u8 u1sel; @@ -1010,6 +1009,8 @@ struct dwc3 { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + unsigned dis_metastability_quirk:1; + u16 imod_interval; }; diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 368f8e59219a..bfb90c52d8fc 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -247,6 +247,15 @@ static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v, case USB_DEVICE_TEST_MODE: s = "Test Mode"; break; + case USB_DEVICE_U1_ENABLE: + s = "U1 Enable"; + break; + case USB_DEVICE_U2_ENABLE: + s = "U2 Enable"; + break; + case USB_DEVICE_LTM_ENABLE: + s = "LTM Enable"; + break; default: s = "UNKNOWN"; } s; }), diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index c4a4d7bd2766..7ae0eefc7cc7 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -51,8 +51,10 @@ static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) clk = of_clk_get(np, i); if (IS_ERR(clk)) { - while (--i >= 0) + while (--i >= 0) { + clk_disable_unprepare(simple->clks[i]); clk_put(simple->clks[i]); + } return PTR_ERR(clk); } @@ -203,6 +205,7 @@ static struct platform_driver dwc3_of_simple_driver = { .driver = { .name = "dwc3-of-simple", .of_match_table = of_dwc3_simple_match, + .pm = &dwc3_of_simple_dev_pm_ops, }, }; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index fd3e7ad2eb0e..9c2e4a17918e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -736,11 +736,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct if (wIndex || wLength) return -EINVAL; - /* - * REVISIT It's unclear from Databook what to do with this - * value. For now, just cache it. - */ - dwc->isoch_delay = wValue; + dwc->gadget.isoch_delay = wValue; return 0; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 981fd986cf82..616ef49ccb49 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -259,7 +259,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, { const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; struct dwc3 *dwc = dep->dwc; - u32 timeout = 500; + u32 timeout = 1000; u32 reg; int cmd_status = 0; @@ -912,7 +912,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, */ if (speed == USB_SPEED_HIGH) { struct usb_ep *ep = &dep->endpoint; - unsigned int mult = ep->mult - 1; + unsigned int mult = 2; unsigned int maxp = usb_endpoint_maxp(ep->desc); if (length <= (2 * maxp)) @@ -2005,7 +2005,8 @@ static void dwc3_gadget_set_speed(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 && + !dwc->dis_metastability_quirk) { reg |= DWC3_DCFG_SUPERSPEED; } else { switch (speed) { @@ -3224,7 +3225,8 @@ int dwc3_gadget_init(struct dwc3 *dwc) * is less than super speed because we don't have means, yet, to tell * composite.c that we are USB 2.0 + LPM ECN. */ - if (dwc->revision < DWC3_REVISION_220A) + if (dwc->revision < DWC3_REVISION_220A && + !dwc->dis_metastability_quirk) dev_info(dwc->dev, "changing max_speed on rev %08x\n", dwc->revision); diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index a9dd5c64e6c7..babaee981aa7 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -37,12 +37,12 @@ DECLARE_EVENT_CLASS(dwc3_log_io, ); DEFINE_EVENT(dwc3_log_io, dwc3_readl, - TP_PROTO(void *base, u32 offset, u32 value), + TP_PROTO(void __iomem *base, u32 offset, u32 value), TP_ARGS(base, offset, value) ); DEFINE_EVENT(dwc3_log_io, dwc3_writel, - TP_PROTO(void *base, u32 offset, u32 value), + TP_PROTO(void __iomem *base, u32 offset, u32 value), TP_ARGS(base, offset, value) ); diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c index 8a700b45b9a9..e15e896f356c 100644 --- a/drivers/usb/early/xhci-dbc.c +++ b/drivers/usb/early/xhci-dbc.c @@ -328,7 +328,7 @@ static void xdbc_mem_init(void) ep_in = (struct xdbc_ep_context *)&ctx->in; ep_in->ep_info1 = 0; - ep_in->ep_info2 = cpu_to_le32(EP_TYPE(BULK_OUT_EP) | MAX_PACKET(1024) | MAX_BURST(max_burst)); + ep_in->ep_info2 = cpu_to_le32(EP_TYPE(BULK_IN_EP) | MAX_PACKET(1024) | MAX_BURST(max_burst)); ep_in->deq = cpu_to_le64(xdbc.in_seg.dma | xdbc.in_ring.cycle_state); /* Set DbC context and info registers: */ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index eec14e6ed20b..77c7ecca816a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -146,7 +146,6 @@ int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f, struct usb_ep *_ep) { - struct usb_composite_dev *cdev = get_gadget_data(g); struct usb_endpoint_descriptor *chosen_desc = NULL; struct usb_descriptor_header **speed_desc = NULL; @@ -226,8 +225,12 @@ ep_found: _ep->maxburst = comp_desc->bMaxBurst + 1; break; default: - if (comp_desc->bMaxBurst != 0) + if (comp_desc->bMaxBurst != 0) { + struct usb_composite_dev *cdev; + + cdev = get_gadget_data(g); ERROR(cdev, "ep0 bMaxBurst must be 0\n"); + } _ep->maxburst = 1; break; } diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 97ea059a7aa4..686af89323a5 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -266,6 +266,7 @@ static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req) } static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) + __releases(&ffs->ev.waitq.lock) { struct usb_request *req = ffs->ep0req; int ret; @@ -458,6 +459,7 @@ done_spin: /* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf, size_t n) + __releases(&ffs->ev.waitq.lock) { /* * n cannot be bigger than ffs->ev.count, which cannot be bigger than @@ -543,6 +545,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, break; } + /* unlocks spinlock */ return __ffs_ep0_read_events(ffs, buf, min(n, (size_t)ffs->ev.count)); @@ -1012,7 +1015,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) else ret = ep->status; goto error_mutex; - } else if (!(req = usb_ep_alloc_request(ep->ep, GFP_KERNEL))) { + } else if (!(req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC))) { ret = -ENOMEM; } else { req->buf = data; @@ -1246,7 +1249,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, desc = epfile->ep->descs[desc_idx]; spin_unlock_irq(&epfile->ffs->eps_lock); - ret = copy_to_user((void *)value, desc, desc->bLength); + ret = copy_to_user((void __user *)value, desc, desc->bLength); if (ret) ret = -EFAULT; return ret; @@ -2282,9 +2285,18 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, int i; if (len < sizeof(*d) || - d->bFirstInterfaceNumber >= ffs->interfaces_count || - !d->Reserved1) + d->bFirstInterfaceNumber >= ffs->interfaces_count) return -EINVAL; + if (d->Reserved1 != 1) { + /* + * According to the spec, Reserved1 must be set to 1 + * but older kernels incorrectly rejected non-zero + * values. We fix it here to avoid returning EINVAL + * in response to values we used to accept. + */ + pr_debug("usb_ext_compat_desc::Reserved1 forced to 1\n"); + d->Reserved1 = 1; + } for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i) if (d->Reserved2[i]) return -EINVAL; @@ -2315,7 +2327,7 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, length, pnl, type); return -EINVAL; } - pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl)); + pdl = le32_to_cpu(*(__le32 *)((u8 *)data + 10 + pnl)); if (length != 14 + pnl + pdl) { pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n", length, pnl, pdl, type); @@ -2869,7 +2881,7 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type, ext_prop->type = le32_to_cpu(desc->dwPropertyDataType); ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength); - ext_prop->data_len = le32_to_cpu(*(u32 *) + ext_prop->data_len = le32_to_cpu(*(__le32 *) usb_ext_prop_data_len_ptr(data, ext_prop->name_len)); length = ext_prop->name_len + ext_prop->data_len + 14; @@ -3691,7 +3703,8 @@ static void ffs_closed(struct ffs_data *ffs) ci = opts->func_inst.group.cg_item.ci_parent->ci_parent; ffs_dev_unlock(); - unregister_gadget_item(ci); + if (test_bit(FFS_FL_BOUND, &ffs->flags)) + unregister_gadget_item(ci); return; done: ffs_dev_unlock(); diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 4d653d2960d4..29436f75bbe0 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/console.h> #include <linux/kthread.h> +#include <linux/kfifo.h> #include "u_serial.h" @@ -80,19 +81,11 @@ #define WRITE_BUF_SIZE 8192 /* TX only */ #define GS_CONSOLE_BUF_SIZE 8192 -/* circular buffer */ -struct gs_buf { - unsigned buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; - /* console info */ struct gscons_info { struct gs_port *port; struct task_struct *console_thread; - struct gs_buf con_buf; + struct kfifo con_buf; /* protect the buf and busy flag */ spinlock_t con_lock; int req_busy; @@ -122,7 +115,7 @@ struct gs_port { struct list_head write_pool; int write_started; int write_allocated; - struct gs_buf port_write_buf; + struct kfifo port_write_buf; wait_queue_head_t drain_wait; /* wait while writes drain */ bool write_busy; wait_queue_head_t close_wait; @@ -154,144 +147,6 @@ static struct portmaster { /*-------------------------------------------------------------------------*/ -/* Circular Buffer */ - -/* - * gs_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ -static int gs_buf_alloc(struct gs_buf *gb, unsigned size) -{ - gb->buf_buf = kmalloc(size, GFP_KERNEL); - if (gb->buf_buf == NULL) - return -ENOMEM; - - gb->buf_size = size; - gb->buf_put = gb->buf_buf; - gb->buf_get = gb->buf_buf; - - return 0; -} - -/* - * gs_buf_free - * - * Free the buffer and all associated memory. - */ -static void gs_buf_free(struct gs_buf *gb) -{ - kfree(gb->buf_buf); - gb->buf_buf = NULL; -} - -/* - * gs_buf_clear - * - * Clear out all data in the circular buffer. - */ -static void gs_buf_clear(struct gs_buf *gb) -{ - gb->buf_get = gb->buf_put; - /* equivalent to a get of all data available */ -} - -/* - * gs_buf_data_avail - * - * Return the number of bytes of data written into the circular - * buffer. - */ -static unsigned gs_buf_data_avail(struct gs_buf *gb) -{ - return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; -} - -/* - * gs_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ -static unsigned gs_buf_space_avail(struct gs_buf *gb) -{ - return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; -} - -/* - * gs_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ -static unsigned -gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count) -{ - unsigned len; - - len = gs_buf_space_avail(gb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = gb->buf_buf + gb->buf_size - gb->buf_put; - if (count > len) { - memcpy(gb->buf_put, buf, len); - memcpy(gb->buf_buf, buf+len, count - len); - gb->buf_put = gb->buf_buf + count - len; - } else { - memcpy(gb->buf_put, buf, count); - if (count < len) - gb->buf_put += count; - else /* count == len */ - gb->buf_put = gb->buf_buf; - } - - return count; -} - -/* - * gs_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ -static unsigned -gs_buf_get(struct gs_buf *gb, char *buf, unsigned count) -{ - unsigned len; - - len = gs_buf_data_avail(gb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = gb->buf_buf + gb->buf_size - gb->buf_get; - if (count > len) { - memcpy(buf, gb->buf_get, len); - memcpy(buf+len, gb->buf_buf, count - len); - gb->buf_get = gb->buf_buf + count - len; - } else { - memcpy(buf, gb->buf_get, count); - if (count < len) - gb->buf_get += count; - else /* count == len */ - gb->buf_get = gb->buf_buf; - } - - return count; -} - -/*-------------------------------------------------------------------------*/ - /* I/O glue between TTY (upper) and USB function (lower) driver layers */ /* @@ -346,11 +201,11 @@ gs_send_packet(struct gs_port *port, char *packet, unsigned size) { unsigned len; - len = gs_buf_data_avail(&port->port_write_buf); + len = kfifo_len(&port->port_write_buf); if (len < size) size = len; if (size != 0) - size = gs_buf_get(&port->port_write_buf, packet, size); + size = kfifo_out(&port->port_write_buf, packet, size); return size; } @@ -398,7 +253,7 @@ __acquires(&port->port_lock) req->length = len; list_del(&req->list); - req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0); + req->zero = kfifo_is_empty(&port->port_write_buf); pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", port->port_num, len, *((u8 *)req->buf), @@ -787,10 +642,11 @@ static int gs_open(struct tty_struct *tty, struct file *file) spin_lock_irq(&port->port_lock); /* allocate circular buffer on first open */ - if (port->port_write_buf.buf_buf == NULL) { + if (!kfifo_initialized(&port->port_write_buf)) { spin_unlock_irq(&port->port_lock); - status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE); + status = kfifo_alloc(&port->port_write_buf, + WRITE_BUF_SIZE, GFP_KERNEL); spin_lock_irq(&port->port_lock); if (status) { @@ -839,7 +695,7 @@ static int gs_writes_finished(struct gs_port *p) /* return true on disconnect or empty buffer */ spin_lock_irq(&p->port_lock); - cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf); + cond = (p->port_usb == NULL) || !kfifo_len(&p->port_write_buf); spin_unlock_irq(&p->port_lock); return cond; @@ -875,7 +731,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) /* wait for circular write buffer to drain, disconnect, or at * most GS_CLOSE_TIMEOUT seconds; then discard the rest */ - if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) { + if (kfifo_len(&port->port_write_buf) > 0 && gser) { spin_unlock_irq(&port->port_lock); wait_event_interruptible_timeout(port->drain_wait, gs_writes_finished(port), @@ -889,9 +745,9 @@ static void gs_close(struct tty_struct *tty, struct file *file) * let the push tasklet fire again until we're re-opened. */ if (gser == NULL) - gs_buf_free(&port->port_write_buf); + kfifo_free(&port->port_write_buf); else - gs_buf_clear(&port->port_write_buf); + kfifo_reset(&port->port_write_buf); port->port.tty = NULL; @@ -915,7 +771,7 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) spin_lock_irqsave(&port->port_lock, flags); if (count) - count = gs_buf_put(&port->port_write_buf, buf, count); + count = kfifo_in(&port->port_write_buf, buf, count); /* treat count == 0 as flush_chars() */ if (port->port_usb) gs_start_tx(port); @@ -934,7 +790,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch) port->port_num, tty, ch, __builtin_return_address(0)); spin_lock_irqsave(&port->port_lock, flags); - status = gs_buf_put(&port->port_write_buf, &ch, 1); + status = kfifo_put(&port->port_write_buf, ch); spin_unlock_irqrestore(&port->port_lock, flags); return status; @@ -961,7 +817,7 @@ static int gs_write_room(struct tty_struct *tty) spin_lock_irqsave(&port->port_lock, flags); if (port->port_usb) - room = gs_buf_space_avail(&port->port_write_buf); + room = kfifo_avail(&port->port_write_buf); spin_unlock_irqrestore(&port->port_lock, flags); pr_vdebug("gs_write_room: (%d,%p) room=%d\n", @@ -977,7 +833,7 @@ static int gs_chars_in_buffer(struct tty_struct *tty) int chars = 0; spin_lock_irqsave(&port->port_lock, flags); - chars = gs_buf_data_avail(&port->port_write_buf); + chars = kfifo_len(&port->port_write_buf); spin_unlock_irqrestore(&port->port_lock, flags); pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n", @@ -1148,7 +1004,7 @@ static int gs_console_thread(void *data) ep = port->port_usb->in; spin_lock_irq(&info->con_lock); - count = gs_buf_data_avail(&info->con_buf); + count = kfifo_len(&info->con_buf); size = ep->maxpacket; if (count > 0 && !info->req_busy) { @@ -1156,7 +1012,7 @@ static int gs_console_thread(void *data) if (count < size) size = count; - xfer = gs_buf_get(&info->con_buf, req->buf, size); + xfer = kfifo_out(&info->con_buf, req->buf, size); req->length = xfer; spin_unlock(&info->con_lock); @@ -1192,7 +1048,7 @@ static int gs_console_setup(struct console *co, char *options) info->req_busy = 0; spin_lock_init(&info->con_lock); - status = gs_buf_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE); + status = kfifo_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL); if (status) { pr_err("%s: allocate console buffer failed\n", __func__); return status; @@ -1202,7 +1058,7 @@ static int gs_console_setup(struct console *co, char *options) co, "gs_console"); if (IS_ERR(info->console_thread)) { pr_err("%s: cannot create console thread\n", __func__); - gs_buf_free(&info->con_buf); + kfifo_free(&info->con_buf); return PTR_ERR(info->console_thread); } wake_up_process(info->console_thread); @@ -1217,7 +1073,7 @@ static void gs_console_write(struct console *co, unsigned long flags; spin_lock_irqsave(&info->con_lock, flags); - gs_buf_put(&info->con_buf, buf, count); + kfifo_in(&info->con_buf, buf, count); spin_unlock_irqrestore(&info->con_lock, flags); wake_up_process(info->console_thread); @@ -1256,7 +1112,7 @@ static void gserial_console_exit(void) unregister_console(&gserial_cons); if (!IS_ERR_OR_NULL(info->console_thread)) kthread_stop(info->console_thread); - gs_buf_free(&info->con_buf); + kfifo_free(&info->con_buf); } #else @@ -1529,7 +1385,7 @@ void gserial_disconnect(struct gserial *gser) /* finally, free any unused/unusable I/O buffers */ spin_lock_irqsave(&port->port_lock, flags); if (port->port.count == 0 && !port->openclose) - gs_buf_free(&port->port_write_buf); + kfifo_free(&port->port_write_buf); gs_free_requests(gser->out, &port->read_pool, NULL); gs_free_requests(gser->out, &port->read_queue, NULL); gs_free_requests(gser->in, &port->write_pool, NULL); diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index a12fb459dbd9..784bf86dad4f 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -479,7 +479,7 @@ endif # or video class gadget drivers), or specific hardware, here. config USB_G_WEBCAM tristate "USB Webcam Gadget" - depends on VIDEO_DEV + depends on VIDEO_V4L2 select USB_LIBCOMPOSITE select VIDEOBUF2_VMALLOC select USB_F_UVC diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 9343ec436485..cb8e1761d405 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1470,7 +1470,6 @@ delegate: dev->setup_wLength = w_length; dev->setup_out_ready = 0; dev->setup_out_error = 0; - value = 0; /* read DATA stage for OUT right away */ if (unlikely (!dev->setup_in && w_length)) { diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index 29f254793592..465ccd1104de 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2385,10 +2385,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) goto out_uninit; } if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0, - dev_name(dev), udc) < 0) { - dev_err(dev, "error requesting IRQ #%d\n", irq); - goto out_uninit; - } + dev_name(dev), udc) < 0) + goto report_request_failure; /* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) { @@ -2398,10 +2396,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) goto out_uninit; } if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0, - dev_name(dev), &udc->iudma[i]) < 0) { - dev_err(dev, "error requesting IRQ #%d\n", irq); - goto out_uninit; - } + dev_name(dev), &udc->iudma[i]) < 0) + goto report_request_failure; } bcm63xx_udc_init_debugfs(udc); @@ -2413,6 +2409,10 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) out_uninit: bcm63xx_uninit_udc_hw(udc); return rc; + +report_request_failure: + dev_err(dev, "error requesting IRQ #%d\n", irq); + goto out_uninit; } /** diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index d39f070acbd7..01b44e159623 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -642,7 +642,6 @@ static const struct of_device_id bdc_of_match[] = { static struct platform_driver bdc_driver = { .driver = { .name = BRCM_BDC_NAME, - .owner = THIS_MODULE, .pm = &bdc_pm_ops, .of_match_table = bdc_of_match, }, diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 61422d624ad0..93eff7dec2f5 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1069,8 +1069,12 @@ static inline void usb_gadget_udc_stop(struct usb_udc *udc) static inline void usb_gadget_udc_set_speed(struct usb_udc *udc, enum usb_device_speed speed) { - if (udc->gadget->ops->udc_set_speed) - udc->gadget->ops->udc_set_speed(udc->gadget, speed); + if (udc->gadget->ops->udc_set_speed) { + enum usb_device_speed s; + + s = min(speed, udc->gadget->max_speed); + udc->gadget->ops->udc_set_speed(udc->gadget, s); + } } /** diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index d0128f92ec5a..e744d4b7bfed 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -925,20 +925,8 @@ static void dummy_udc_set_speed(struct usb_gadget *_gadget, struct dummy *dum; dum = gadget_dev_to_dummy(&_gadget->dev); - - if (mod_data.is_super_speed) - dum->gadget.speed = min_t(u8, USB_SPEED_SUPER, speed); - else if (mod_data.is_high_speed) - dum->gadget.speed = min_t(u8, USB_SPEED_HIGH, speed); - else - dum->gadget.speed = USB_SPEED_FULL; - + dum->gadget.speed = speed; dummy_udc_update_ep0(dum); - - if (dum->gadget.speed < speed) - dev_dbg(udc_dev(dum), "This device can perform faster" - " if you connect it to a %s port...\n", - usb_speed_string(speed)); } static int dummy_udc_start(struct usb_gadget *g, @@ -2193,8 +2181,6 @@ static int dummy_hub_control( USB_PORT_STAT_LOW_SPEED; break; default: - dum_hcd->dum->gadget.speed = - USB_SPEED_FULL; break; } } diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index d606d4f13098..e5b4ee96c4bf 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1543,7 +1543,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, udc->ep0_state = WAIT_FOR_SETUP; break; case WAIT_FOR_SETUP: - ERR("Unexpect ep0 packets\n"); + ERR("Unexpected ep0 packets\n"); break; default: ep0stall(udc); diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index be2761f1b3f5..fadcf2653c3d 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -979,8 +979,6 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req) max = ep->fifo_size; do { - is_short = 0; - udccsr = udc_ep_readl(ep, UDCCSR); if (udccsr & UDCCSR_PC) { ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n", @@ -1134,7 +1132,6 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, if (unlikely(!_ep)) return -EINVAL; - dev = udc_usb_ep->dev; ep = udc_usb_ep->pxa_ep; if (unlikely(!ep)) return -EINVAL; diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index bc37f40baacf..6e87af248367 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -252,7 +252,7 @@ #define USB3_EP0_SS_MAX_PACKET_SIZE 512 #define USB3_EP0_HSFS_MAX_PACKET_SIZE 64 #define USB3_EP0_BUF_SIZE 8 -#define USB3_MAX_NUM_PIPES 30 +#define USB3_MAX_NUM_PIPES 6 /* This includes PIPE 0 */ #define USB3_WAIT_US 3 #define USB3_DMA_NUM_SETTING_AREA 4 /* diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h index f07ddb3f4bb9..2d1f68b5ea76 100644 --- a/drivers/usb/gadget/udc/trace.h +++ b/drivers/usb/gadget/udc/trace.h @@ -225,6 +225,7 @@ DECLARE_EVENT_CLASS(udc_log_req, __field(unsigned, short_not_ok) __field(int, status) __field(int, ret) + __field(struct usb_request *, req) ), TP_fast_assign( snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name); @@ -238,9 +239,10 @@ DECLARE_EVENT_CLASS(udc_log_req, __entry->short_not_ok = req->short_not_ok; __entry->status = req->status; __entry->ret = ret; + __entry->req = req; ), - TP_printk("%s: length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d", - __get_str(name), __entry->actual, __entry->length, + TP_printk("%s: req %p length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d", + __get_str(name),__entry->req, __entry->actual, __entry->length, __entry->num_mapped_sgs, __entry->num_sgs, __entry->stream_id, __entry->zero ? "Z" : "z", __entry->short_not_ok ? "S" : "s", diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 7da2b9ce8cb3..6407e433bc78 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -963,10 +963,8 @@ static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct xusb_ep *ep = to_xusb_ep(_ep); - struct xusb_udc *udc; struct xusb_req *req; - udc = ep->udc; req = kzalloc(sizeof(*req), gfp_flags); if (!req) return NULL; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index b80a94e632af..6150bed7cfa8 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -27,6 +27,14 @@ config USB_XHCI_HCD module will be called xhci-hcd. if USB_XHCI_HCD +config USB_XHCI_DBGCAP + bool "xHCI support for debug capability" + depends on TTY + ---help--- + Say 'Y' to enable the support for the xHCI debug capability. Make + sure that your xHCI host supports the extended debug capability and + you want a TTY serial device based on the xHCI debug capability + before enabling this option. If unsure, say 'N'. config USB_XHCI_PCI tristate diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 32b036e2ffef..4ede4ce12366 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -14,6 +14,11 @@ 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 + +ifneq ($(CONFIG_USB_XHCI_DBGCAP), ) + xhci-hcd-y += xhci-dbgcap.o xhci-dbgtty.o +endif + ifneq ($(CONFIG_USB_XHCI_MTK), ) xhci-hcd-y += xhci-mtk-sch.o endif diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 19f00424f53e..3ed75aaa09d9 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -827,7 +827,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) default: /* unknown */ break; } - temp = (cap >> 8) & 0xff; + offset = (cap >> 8) & 0xff; } } #endif diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 62fc955085a1..f3e1e7df88a5 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1865,11 +1865,9 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, qh = kzalloc(sizeof(*qh), GFP_ATOMIC); if (!qh) goto done; - qh->hw = (struct fotg210_qh_hw *) - dma_pool_alloc(fotg210->qh_pool, flags, &dma); + qh->hw = dma_pool_zalloc(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); @@ -4121,7 +4119,7 @@ static int itd_urb_transaction(struct fotg210_iso_stream *stream, } else { alloc_itd: spin_unlock_irqrestore(&fotg210->lock, flags); - itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, + itd = dma_pool_zalloc(fotg210->itd_pool, mem_flags, &itd_dma); spin_lock_irqsave(&fotg210->lock, flags); if (!itd) { @@ -4131,7 +4129,6 @@ alloc_itd: } } - memset(itd, 0, sizeof(*itd)); itd->itd_dma = itd_dma; list_add(&itd->itd_list, &sched->td_list); } diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 0c507a0cfe1f..a55cbba40a5a 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -413,7 +413,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev) da8xx_ohci = to_da8xx_ohci(hcd); da8xx_ohci->hcd = hcd; - da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, "usb11"); + da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(da8xx_ohci->usb11_clk)) { error = PTR_ERR(da8xx_ohci->usb11_clk); if (error != -EPROBE_DEFER) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index f5c90217777a..f9c3947577fc 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -600,7 +600,7 @@ static int uhci_start(struct usb_hcd *hcd) uhci->dentry = dentry; #endif - uhci->frame = dma_alloc_coherent(uhci_dev(uhci), + uhci->frame = dma_zalloc_coherent(uhci_dev(uhci), UHCI_NUMFRAMES * sizeof(*uhci->frame), &uhci->frame_dma_handle, GFP_KERNEL); if (!uhci->frame) { @@ -608,7 +608,6 @@ static int uhci_start(struct usb_hcd *hcd) "unable to allocate consistent memory for frame list\n"); goto err_alloc_frame; } - memset(uhci->frame, 0, UHCI_NUMFRAMES * sizeof(*uhci->frame)); uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu), GFP_KERNEL); diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index d40438238938..35fcb826152c 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -73,8 +73,7 @@ static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb) { struct urb_priv *urbp = urb->hcpriv; - if (!(urb->transfer_flags & URB_NO_FSBR)) - urbp->fsbr = 1; + urbp->fsbr = 1; } static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp) diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index c5ac9efb076a..276fb34c8efd 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -90,9 +90,7 @@ static uint32_t process_qset(struct whc *whc, struct whc_qset *qset) while (qset->ntds) { struct whc_qtd *td; - int t; - t = qset->td_start; td = &qset->qtd[qset->td_start]; status = le32_to_cpu(td->status); diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 584d7b9a3683..386abf26641d 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -10,267 +10,6 @@ #include "xhci.h" -#define XHCI_INIT_VALUE 0x0 - -/* Add verbose debugging later, just print everything for now */ - -void xhci_dbg_regs(struct xhci_hcd *xhci) -{ - u32 temp; - - xhci_dbg(xhci, "// xHCI capability registers at %p:\n", - xhci->cap_regs); - temp = readl(&xhci->cap_regs->hc_capbase); - xhci_dbg(xhci, "// @%p = 0x%x (CAPLENGTH AND HCIVERSION)\n", - &xhci->cap_regs->hc_capbase, temp); - xhci_dbg(xhci, "// CAPLENGTH: 0x%x\n", - (unsigned int) HC_LENGTH(temp)); - xhci_dbg(xhci, "// HCIVERSION: 0x%x\n", - (unsigned int) HC_VERSION(temp)); - - xhci_dbg(xhci, "// xHCI operational registers at %p:\n", xhci->op_regs); - - temp = readl(&xhci->cap_regs->run_regs_off); - xhci_dbg(xhci, "// @%p = 0x%x RTSOFF\n", - &xhci->cap_regs->run_regs_off, - (unsigned int) temp & RTSOFF_MASK); - xhci_dbg(xhci, "// xHCI runtime registers at %p:\n", xhci->run_regs); - - temp = readl(&xhci->cap_regs->db_off); - xhci_dbg(xhci, "// @%p = 0x%x DBOFF\n", &xhci->cap_regs->db_off, temp); - xhci_dbg(xhci, "// Doorbell array at %p:\n", xhci->dba); -} - -static void xhci_print_cap_regs(struct xhci_hcd *xhci) -{ - u32 temp; - u32 hci_version; - - xhci_dbg(xhci, "xHCI capability registers at %p:\n", xhci->cap_regs); - - temp = readl(&xhci->cap_regs->hc_capbase); - hci_version = HC_VERSION(temp); - xhci_dbg(xhci, "CAPLENGTH AND HCIVERSION 0x%x:\n", - (unsigned int) temp); - xhci_dbg(xhci, "CAPLENGTH: 0x%x\n", - (unsigned int) HC_LENGTH(temp)); - xhci_dbg(xhci, "HCIVERSION: 0x%x\n", hci_version); - - temp = readl(&xhci->cap_regs->hcs_params1); - xhci_dbg(xhci, "HCSPARAMS 1: 0x%x\n", - (unsigned int) temp); - xhci_dbg(xhci, " Max device slots: %u\n", - (unsigned int) HCS_MAX_SLOTS(temp)); - xhci_dbg(xhci, " Max interrupters: %u\n", - (unsigned int) HCS_MAX_INTRS(temp)); - xhci_dbg(xhci, " Max ports: %u\n", - (unsigned int) HCS_MAX_PORTS(temp)); - - temp = readl(&xhci->cap_regs->hcs_params2); - xhci_dbg(xhci, "HCSPARAMS 2: 0x%x\n", - (unsigned int) temp); - xhci_dbg(xhci, " Isoc scheduling threshold: %u\n", - (unsigned int) HCS_IST(temp)); - xhci_dbg(xhci, " Maximum allowed segments in event ring: %u\n", - (unsigned int) HCS_ERST_MAX(temp)); - - temp = readl(&xhci->cap_regs->hcs_params3); - xhci_dbg(xhci, "HCSPARAMS 3 0x%x:\n", - (unsigned int) temp); - xhci_dbg(xhci, " Worst case U1 device exit latency: %u\n", - (unsigned int) HCS_U1_LATENCY(temp)); - xhci_dbg(xhci, " Worst case U2 device exit latency: %u\n", - (unsigned int) HCS_U2_LATENCY(temp)); - - temp = readl(&xhci->cap_regs->hcc_params); - xhci_dbg(xhci, "HCC PARAMS 0x%x:\n", (unsigned int) temp); - xhci_dbg(xhci, " HC generates %s bit addresses\n", - HCC_64BIT_ADDR(temp) ? "64" : "32"); - xhci_dbg(xhci, " HC %s Contiguous Frame ID Capability\n", - HCC_CFC(temp) ? "has" : "hasn't"); - xhci_dbg(xhci, " HC %s generate Stopped - Short Package event\n", - HCC_SPC(temp) ? "can" : "can't"); - /* FIXME */ - xhci_dbg(xhci, " FIXME: more HCCPARAMS debugging\n"); - - temp = readl(&xhci->cap_regs->run_regs_off); - xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK); - - /* xhci 1.1 controllers have the HCCPARAMS2 register */ - if (hci_version > 0x100) { - temp = readl(&xhci->cap_regs->hcc_params2); - xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp); - xhci_dbg(xhci, " HC %s Force save context capability", - HCC2_FSC(temp) ? "supports" : "doesn't support"); - xhci_dbg(xhci, " HC %s Large ESIT Payload Capability", - HCC2_LEC(temp) ? "supports" : "doesn't support"); - xhci_dbg(xhci, " HC %s Extended TBC capability", - HCC2_ETC(temp) ? "supports" : "doesn't support"); - } -} - -static void xhci_print_command_reg(struct xhci_hcd *xhci) -{ - u32 temp; - - temp = readl(&xhci->op_regs->command); - xhci_dbg(xhci, "USBCMD 0x%x:\n", temp); - xhci_dbg(xhci, " HC is %s\n", - (temp & CMD_RUN) ? "running" : "being stopped"); - xhci_dbg(xhci, " HC has %sfinished hard reset\n", - (temp & CMD_RESET) ? "not " : ""); - xhci_dbg(xhci, " Event Interrupts %s\n", - (temp & CMD_EIE) ? "enabled " : "disabled"); - xhci_dbg(xhci, " Host System Error Interrupts %s\n", - (temp & CMD_HSEIE) ? "enabled " : "disabled"); - xhci_dbg(xhci, " HC has %sfinished light reset\n", - (temp & CMD_LRESET) ? "not " : ""); -} - -static void xhci_print_status(struct xhci_hcd *xhci) -{ - u32 temp; - - temp = readl(&xhci->op_regs->status); - xhci_dbg(xhci, "USBSTS 0x%x:\n", temp); - xhci_dbg(xhci, " Event ring is %sempty\n", - (temp & STS_EINT) ? "not " : ""); - xhci_dbg(xhci, " %sHost System Error\n", - (temp & STS_FATAL) ? "WARNING: " : "No "); - xhci_dbg(xhci, " HC is %s\n", - (temp & STS_HALT) ? "halted" : "running"); -} - -static void xhci_print_op_regs(struct xhci_hcd *xhci) -{ - xhci_dbg(xhci, "xHCI operational registers at %p:\n", xhci->op_regs); - xhci_print_command_reg(xhci); - xhci_print_status(xhci); -} - -static void xhci_print_ports(struct xhci_hcd *xhci) -{ - __le32 __iomem *addr; - int i, j; - int ports; - char *names[NUM_PORT_REGS] = { - "status", - "power", - "link", - "reserved", - }; - - ports = HCS_MAX_PORTS(xhci->hcs_params1); - addr = &xhci->op_regs->port_status_base; - for (i = 0; i < ports; i++) { - for (j = 0; j < NUM_PORT_REGS; j++) { - xhci_dbg(xhci, "%p port %s reg = 0x%x\n", - addr, names[j], - (unsigned int) readl(addr)); - addr++; - } - } -} - -void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num) -{ - struct xhci_intr_reg __iomem *ir_set = &xhci->run_regs->ir_set[set_num]; - void __iomem *addr; - u32 temp; - u64 temp_64; - - addr = &ir_set->irq_pending; - temp = readl(addr); - if (temp == XHCI_INIT_VALUE) - return; - - xhci_dbg(xhci, " %p: ir_set[%i]\n", ir_set, set_num); - - xhci_dbg(xhci, " %p: ir_set.pending = 0x%x\n", addr, - (unsigned int)temp); - - addr = &ir_set->irq_control; - temp = readl(addr); - xhci_dbg(xhci, " %p: ir_set.control = 0x%x\n", addr, - (unsigned int)temp); - - addr = &ir_set->erst_size; - temp = readl(addr); - xhci_dbg(xhci, " %p: ir_set.erst_size = 0x%x\n", addr, - (unsigned int)temp); - - addr = &ir_set->rsvd; - temp = readl(addr); - if (temp != XHCI_INIT_VALUE) - xhci_dbg(xhci, " WARN: %p: ir_set.rsvd = 0x%x\n", - addr, (unsigned int)temp); - - addr = &ir_set->erst_base; - temp_64 = xhci_read_64(xhci, addr); - xhci_dbg(xhci, " %p: ir_set.erst_base = @%08llx\n", - addr, temp_64); - - addr = &ir_set->erst_dequeue; - temp_64 = xhci_read_64(xhci, addr); - xhci_dbg(xhci, " %p: ir_set.erst_dequeue = @%08llx\n", - addr, temp_64); -} - -void xhci_print_run_regs(struct xhci_hcd *xhci) -{ - u32 temp; - int i; - - xhci_dbg(xhci, "xHCI runtime registers at %p:\n", xhci->run_regs); - temp = readl(&xhci->run_regs->microframe_index); - xhci_dbg(xhci, " %p: Microframe index = 0x%x\n", - &xhci->run_regs->microframe_index, - (unsigned int) temp); - for (i = 0; i < 7; i++) { - temp = readl(&xhci->run_regs->rsvd[i]); - if (temp != XHCI_INIT_VALUE) - xhci_dbg(xhci, " WARN: %p: Rsvd[%i] = 0x%x\n", - &xhci->run_regs->rsvd[i], - i, (unsigned int) temp); - } -} - -void xhci_print_registers(struct xhci_hcd *xhci) -{ - xhci_print_cap_regs(xhci); - xhci_print_op_regs(xhci); - xhci_print_ports(xhci); -} - -void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) -{ - u64 addr = erst->erst_dma_addr; - int i; - struct xhci_erst_entry *entry; - - for (i = 0; i < erst->num_entries; i++) { - entry = &erst->entries[i]; - xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", - addr, - lower_32_bits(le64_to_cpu(entry->seg_addr)), - upper_32_bits(le64_to_cpu(entry->seg_addr)), - le32_to_cpu(entry->seg_size), - le32_to_cpu(entry->rsvd)); - addr += sizeof(*entry); - } -} - -void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci) -{ - u64 val; - - val = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); - xhci_dbg(xhci, "// xHC command ring deq ptr low bits + flags = @%08x\n", - lower_32_bits(val)); - xhci_dbg(xhci, "// xHC command ring deq ptr high bits = @%08x\n", - upper_32_bits(val)); -} - char *xhci_get_slot_state(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx) { diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c new file mode 100644 index 000000000000..452df0f87d6e --- /dev/null +++ b/drivers/usb/host/xhci-dbgcap.c @@ -0,0 +1,996 @@ +/** + * xhci-dbgcap.c - xHCI debug capability support + * + * Copyright (C) 2017 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + */ +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/nls.h> + +#include "xhci.h" +#include "xhci-trace.h" +#include "xhci-dbgcap.h" + +static inline void * +dbc_dma_alloc_coherent(struct xhci_hcd *xhci, size_t size, + dma_addr_t *dma_handle, gfp_t flags) +{ + void *vaddr; + + vaddr = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev, + size, dma_handle, flags); + memset(vaddr, 0, size); + return vaddr; +} + +static inline void +dbc_dma_free_coherent(struct xhci_hcd *xhci, size_t size, + void *cpu_addr, dma_addr_t dma_handle) +{ + if (cpu_addr) + dma_free_coherent(xhci_to_hcd(xhci)->self.sysdev, + size, cpu_addr, dma_handle); +} + +static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings) +{ + struct usb_string_descriptor *s_desc; + u32 string_length; + + /* Serial string: */ + s_desc = (struct usb_string_descriptor *)strings->serial; + utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL), + UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, + DBC_MAX_STRING_LENGTH); + + s_desc->bLength = (strlen(DBC_STRING_SERIAL) + 1) * 2; + s_desc->bDescriptorType = USB_DT_STRING; + string_length = s_desc->bLength; + string_length <<= 8; + + /* Product string: */ + s_desc = (struct usb_string_descriptor *)strings->product; + utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT), + UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, + DBC_MAX_STRING_LENGTH); + + s_desc->bLength = (strlen(DBC_STRING_PRODUCT) + 1) * 2; + s_desc->bDescriptorType = USB_DT_STRING; + string_length += s_desc->bLength; + string_length <<= 8; + + /* Manufacture string: */ + s_desc = (struct usb_string_descriptor *)strings->manufacturer; + utf8s_to_utf16s(DBC_STRING_MANUFACTURER, + strlen(DBC_STRING_MANUFACTURER), + UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, + DBC_MAX_STRING_LENGTH); + + s_desc->bLength = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2; + s_desc->bDescriptorType = USB_DT_STRING; + string_length += s_desc->bLength; + string_length <<= 8; + + /* String0: */ + strings->string0[0] = 4; + strings->string0[1] = USB_DT_STRING; + strings->string0[2] = 0x09; + strings->string0[3] = 0x04; + string_length += 4; + + return string_length; +} + +static void xhci_dbc_init_contexts(struct xhci_hcd *xhci, u32 string_length) +{ + struct xhci_dbc *dbc; + struct dbc_info_context *info; + struct xhci_ep_ctx *ep_ctx; + u32 dev_info; + dma_addr_t deq, dma; + unsigned int max_burst; + + dbc = xhci->dbc; + if (!dbc) + return; + + /* Populate info Context: */ + info = (struct dbc_info_context *)dbc->ctx->bytes; + dma = dbc->string_dma; + info->string0 = cpu_to_le64(dma); + info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH); + info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2); + info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3); + info->length = cpu_to_le32(string_length); + + /* Populate bulk out endpoint context: */ + ep_ctx = dbc_bulkout_ctx(dbc); + max_burst = DBC_CTRL_MAXBURST(readl(&dbc->regs->control)); + deq = dbc_bulkout_enq(dbc); + ep_ctx->ep_info = 0; + ep_ctx->ep_info2 = dbc_epctx_info2(BULK_OUT_EP, 1024, max_burst); + ep_ctx->deq = cpu_to_le64(deq | dbc->ring_out->cycle_state); + + /* Populate bulk in endpoint context: */ + ep_ctx = dbc_bulkin_ctx(dbc); + deq = dbc_bulkin_enq(dbc); + ep_ctx->ep_info = 0; + ep_ctx->ep_info2 = dbc_epctx_info2(BULK_IN_EP, 1024, max_burst); + ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state); + + /* Set DbC context and info registers: */ + xhci_write_64(xhci, dbc->ctx->dma, &dbc->regs->dccp); + + dev_info = cpu_to_le32((DBC_VENDOR_ID << 16) | DBC_PROTOCOL); + writel(dev_info, &dbc->regs->devinfo1); + + dev_info = cpu_to_le32((DBC_DEVICE_REV << 16) | DBC_PRODUCT_ID); + writel(dev_info, &dbc->regs->devinfo2); +} + +static void xhci_dbc_giveback(struct dbc_request *req, int status) + __releases(&dbc->lock) + __acquires(&dbc->lock) +{ + struct dbc_ep *dep = req->dep; + struct xhci_dbc *dbc = dep->dbc; + struct xhci_hcd *xhci = dbc->xhci; + struct device *dev = xhci_to_hcd(dbc->xhci)->self.sysdev; + + list_del_init(&req->list_pending); + req->trb_dma = 0; + req->trb = NULL; + + if (req->status == -EINPROGRESS) + req->status = status; + + trace_xhci_dbc_giveback_request(req); + + dma_unmap_single(dev, + req->dma, + req->length, + dbc_ep_dma_direction(dep)); + + /* Give back the transfer request: */ + spin_unlock(&dbc->lock); + req->complete(xhci, req); + spin_lock(&dbc->lock); +} + +static void xhci_dbc_flush_single_request(struct dbc_request *req) +{ + union xhci_trb *trb = req->trb; + + trb->generic.field[0] = 0; + trb->generic.field[1] = 0; + trb->generic.field[2] = 0; + trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE); + trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP)); + + xhci_dbc_giveback(req, -ESHUTDOWN); +} + +static void xhci_dbc_flush_endpoint_requests(struct dbc_ep *dep) +{ + struct dbc_request *req, *tmp; + + list_for_each_entry_safe(req, tmp, &dep->list_pending, list_pending) + xhci_dbc_flush_single_request(req); +} + +static void xhci_dbc_flush_reqests(struct xhci_dbc *dbc) +{ + xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_OUT]); + xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_IN]); +} + +struct dbc_request * +dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags) +{ + struct dbc_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + req->dep = dep; + INIT_LIST_HEAD(&req->list_pending); + INIT_LIST_HEAD(&req->list_pool); + req->direction = dep->direction; + + trace_xhci_dbc_alloc_request(req); + + return req; +} + +void +dbc_free_request(struct dbc_ep *dep, struct dbc_request *req) +{ + trace_xhci_dbc_free_request(req); + + kfree(req); +} + +static void +xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1, + u32 field2, u32 field3, u32 field4) +{ + union xhci_trb *trb, *next; + + trb = ring->enqueue; + trb->generic.field[0] = cpu_to_le32(field1); + trb->generic.field[1] = cpu_to_le32(field2); + trb->generic.field[2] = cpu_to_le32(field3); + trb->generic.field[3] = cpu_to_le32(field4); + + trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic); + + ring->num_trbs_free--; + next = ++(ring->enqueue); + if (TRB_TYPE_LINK_LE32(next->link.control)) { + next->link.control ^= cpu_to_le32(TRB_CYCLE); + ring->enqueue = ring->enq_seg->trbs; + ring->cycle_state ^= 1; + } +} + +static int xhci_dbc_queue_bulk_tx(struct dbc_ep *dep, + struct dbc_request *req) +{ + u64 addr; + union xhci_trb *trb; + unsigned int num_trbs; + struct xhci_dbc *dbc = dep->dbc; + struct xhci_ring *ring = dep->ring; + u32 length, control, cycle; + + num_trbs = count_trbs(req->dma, req->length); + WARN_ON(num_trbs != 1); + if (ring->num_trbs_free < num_trbs) + return -EBUSY; + + addr = req->dma; + trb = ring->enqueue; + cycle = ring->cycle_state; + length = TRB_LEN(req->length); + control = TRB_TYPE(TRB_NORMAL) | TRB_IOC; + + if (cycle) + control &= cpu_to_le32(~TRB_CYCLE); + else + control |= cpu_to_le32(TRB_CYCLE); + + req->trb = ring->enqueue; + req->trb_dma = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); + xhci_dbc_queue_trb(ring, + lower_32_bits(addr), + upper_32_bits(addr), + length, control); + + /* + * Add a barrier between writes of trb fields and flipping + * the cycle bit: + */ + wmb(); + + if (cycle) + trb->generic.field[3] |= cpu_to_le32(TRB_CYCLE); + else + trb->generic.field[3] &= cpu_to_le32(~TRB_CYCLE); + + writel(DBC_DOOR_BELL_TARGET(dep->direction), &dbc->regs->doorbell); + + return 0; +} + +static int +dbc_ep_do_queue(struct dbc_ep *dep, struct dbc_request *req) +{ + int ret; + struct device *dev; + struct xhci_dbc *dbc = dep->dbc; + struct xhci_hcd *xhci = dbc->xhci; + + dev = xhci_to_hcd(xhci)->self.sysdev; + + if (!req->length || !req->buf) + return -EINVAL; + + req->actual = 0; + req->status = -EINPROGRESS; + + req->dma = dma_map_single(dev, + req->buf, + req->length, + dbc_ep_dma_direction(dep)); + if (dma_mapping_error(dev, req->dma)) { + xhci_err(xhci, "failed to map buffer\n"); + return -EFAULT; + } + + ret = xhci_dbc_queue_bulk_tx(dep, req); + if (ret) { + xhci_err(xhci, "failed to queue trbs\n"); + dma_unmap_single(dev, + req->dma, + req->length, + dbc_ep_dma_direction(dep)); + return -EFAULT; + } + + list_add_tail(&req->list_pending, &dep->list_pending); + + return 0; +} + +int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req, + gfp_t gfp_flags) +{ + struct xhci_dbc *dbc = dep->dbc; + int ret = -ESHUTDOWN; + + spin_lock(&dbc->lock); + if (dbc->state == DS_CONFIGURED) + ret = dbc_ep_do_queue(dep, req); + spin_unlock(&dbc->lock); + + mod_delayed_work(system_wq, &dbc->event_work, 0); + + trace_xhci_dbc_queue_request(req); + + return ret; +} + +static inline void xhci_dbc_do_eps_init(struct xhci_hcd *xhci, bool direction) +{ + struct dbc_ep *dep; + struct xhci_dbc *dbc = xhci->dbc; + + dep = &dbc->eps[direction]; + dep->dbc = dbc; + dep->direction = direction; + dep->ring = direction ? dbc->ring_in : dbc->ring_out; + + INIT_LIST_HEAD(&dep->list_pending); +} + +static void xhci_dbc_eps_init(struct xhci_hcd *xhci) +{ + xhci_dbc_do_eps_init(xhci, BULK_OUT); + xhci_dbc_do_eps_init(xhci, BULK_IN); +} + +static void xhci_dbc_eps_exit(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + memset(dbc->eps, 0, sizeof(struct dbc_ep) * ARRAY_SIZE(dbc->eps)); +} + +static int xhci_dbc_mem_init(struct xhci_hcd *xhci, gfp_t flags) +{ + int ret; + dma_addr_t deq; + u32 string_length; + struct xhci_dbc *dbc = xhci->dbc; + + /* Allocate various rings for events and transfers: */ + dbc->ring_evt = xhci_ring_alloc(xhci, 1, 1, TYPE_EVENT, 0, flags); + if (!dbc->ring_evt) + goto evt_fail; + + dbc->ring_in = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags); + if (!dbc->ring_in) + goto in_fail; + + dbc->ring_out = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags); + if (!dbc->ring_out) + goto out_fail; + + /* Allocate and populate ERST: */ + ret = xhci_alloc_erst(xhci, dbc->ring_evt, &dbc->erst, flags); + if (ret) + goto erst_fail; + + /* Allocate context data structure: */ + dbc->ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags); + if (!dbc->ctx) + goto ctx_fail; + + /* Allocate the string table: */ + dbc->string_size = sizeof(struct dbc_str_descs); + dbc->string = dbc_dma_alloc_coherent(xhci, + dbc->string_size, + &dbc->string_dma, + flags); + if (!dbc->string) + goto string_fail; + + /* Setup ERST register: */ + writel(dbc->erst.erst_size, &dbc->regs->ersts); + xhci_write_64(xhci, dbc->erst.erst_dma_addr, &dbc->regs->erstba); + deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg, + dbc->ring_evt->dequeue); + xhci_write_64(xhci, deq, &dbc->regs->erdp); + + /* Setup strings and contexts: */ + string_length = xhci_dbc_populate_strings(dbc->string); + xhci_dbc_init_contexts(xhci, string_length); + + mmiowb(); + + xhci_dbc_eps_init(xhci); + dbc->state = DS_INITIALIZED; + + return 0; + +string_fail: + xhci_free_container_ctx(xhci, dbc->ctx); + dbc->ctx = NULL; +ctx_fail: + xhci_free_erst(xhci, &dbc->erst); +erst_fail: + xhci_ring_free(xhci, dbc->ring_out); + dbc->ring_out = NULL; +out_fail: + xhci_ring_free(xhci, dbc->ring_in); + dbc->ring_in = NULL; +in_fail: + xhci_ring_free(xhci, dbc->ring_evt); + dbc->ring_evt = NULL; +evt_fail: + return -ENOMEM; +} + +static void xhci_dbc_mem_cleanup(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + if (!dbc) + return; + + xhci_dbc_eps_exit(xhci); + + if (dbc->string) { + dbc_dma_free_coherent(xhci, + dbc->string_size, + dbc->string, dbc->string_dma); + dbc->string = NULL; + } + + xhci_free_container_ctx(xhci, dbc->ctx); + dbc->ctx = NULL; + + xhci_free_erst(xhci, &dbc->erst); + xhci_ring_free(xhci, dbc->ring_out); + xhci_ring_free(xhci, dbc->ring_in); + xhci_ring_free(xhci, dbc->ring_evt); + dbc->ring_in = NULL; + dbc->ring_out = NULL; + dbc->ring_evt = NULL; +} + +static int xhci_do_dbc_start(struct xhci_hcd *xhci) +{ + int ret; + u32 ctrl; + struct xhci_dbc *dbc = xhci->dbc; + + if (dbc->state != DS_DISABLED) + return -EINVAL; + + writel(0, &dbc->regs->control); + ret = xhci_handshake(&dbc->regs->control, + DBC_CTRL_DBC_ENABLE, + 0, 1000); + if (ret) + return ret; + + ret = xhci_dbc_mem_init(xhci, GFP_ATOMIC); + if (ret) + return ret; + + ctrl = readl(&dbc->regs->control); + writel(ctrl | DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE, + &dbc->regs->control); + ret = xhci_handshake(&dbc->regs->control, + DBC_CTRL_DBC_ENABLE, + DBC_CTRL_DBC_ENABLE, 1000); + if (ret) + return ret; + + dbc->state = DS_ENABLED; + + return 0; +} + +static void xhci_do_dbc_stop(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + if (dbc->state == DS_DISABLED) + return; + + writel(0, &dbc->regs->control); + xhci_dbc_mem_cleanup(xhci); + dbc->state = DS_DISABLED; +} + +static int xhci_dbc_start(struct xhci_hcd *xhci) +{ + int ret; + struct xhci_dbc *dbc = xhci->dbc; + + WARN_ON(!dbc); + + pm_runtime_get_sync(xhci_to_hcd(xhci)->self.controller); + + spin_lock(&dbc->lock); + ret = xhci_do_dbc_start(xhci); + spin_unlock(&dbc->lock); + + if (ret) { + pm_runtime_put(xhci_to_hcd(xhci)->self.controller); + return ret; + } + + return mod_delayed_work(system_wq, &dbc->event_work, 1); +} + +static void xhci_dbc_stop(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + WARN_ON(!dbc); + + cancel_delayed_work_sync(&dbc->event_work); + + if (port->registered) + xhci_dbc_tty_unregister_device(xhci); + + spin_lock(&dbc->lock); + xhci_do_dbc_stop(xhci); + spin_unlock(&dbc->lock); + + pm_runtime_put_sync(xhci_to_hcd(xhci)->self.controller); +} + +static void +dbc_handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) +{ + u32 portsc; + struct xhci_dbc *dbc = xhci->dbc; + + portsc = readl(&dbc->regs->portsc); + if (portsc & DBC_PORTSC_CONN_CHANGE) + xhci_info(xhci, "DbC port connect change\n"); + + if (portsc & DBC_PORTSC_RESET_CHANGE) + xhci_info(xhci, "DbC port reset change\n"); + + if (portsc & DBC_PORTSC_LINK_CHANGE) + xhci_info(xhci, "DbC port link status change\n"); + + if (portsc & DBC_PORTSC_CONFIG_CHANGE) + xhci_info(xhci, "DbC config error change\n"); + + /* Port reset change bit will be cleared in other place: */ + writel(portsc & ~DBC_PORTSC_RESET_CHANGE, &dbc->regs->portsc); +} + +static void dbc_handle_xfer_event(struct xhci_hcd *xhci, union xhci_trb *event) +{ + struct dbc_ep *dep; + struct xhci_ring *ring; + int ep_id; + int status; + u32 comp_code; + size_t remain_length; + struct dbc_request *req = NULL, *r; + + comp_code = GET_COMP_CODE(le32_to_cpu(event->generic.field[2])); + remain_length = EVENT_TRB_LEN(le32_to_cpu(event->generic.field[2])); + ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3])); + dep = (ep_id == EPID_OUT) ? + get_out_ep(xhci) : get_in_ep(xhci); + ring = dep->ring; + + switch (comp_code) { + case COMP_SUCCESS: + remain_length = 0; + /* FALLTHROUGH */ + case COMP_SHORT_PACKET: + status = 0; + break; + case COMP_TRB_ERROR: + case COMP_BABBLE_DETECTED_ERROR: + case COMP_USB_TRANSACTION_ERROR: + case COMP_STALL_ERROR: + xhci_warn(xhci, "tx error %d detected\n", comp_code); + status = -comp_code; + break; + default: + xhci_err(xhci, "unknown tx error %d\n", comp_code); + status = -comp_code; + break; + } + + /* Match the pending request: */ + list_for_each_entry(r, &dep->list_pending, list_pending) { + if (r->trb_dma == event->trans_event.buffer) { + req = r; + break; + } + } + + if (!req) { + xhci_warn(xhci, "no matched request\n"); + return; + } + + trace_xhci_dbc_handle_transfer(ring, &req->trb->generic); + + ring->num_trbs_free++; + req->actual = req->length - remain_length; + xhci_dbc_giveback(req, status); +} + +static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) +{ + dma_addr_t deq; + struct dbc_ep *dep; + union xhci_trb *evt; + u32 ctrl, portsc; + struct xhci_hcd *xhci = dbc->xhci; + bool update_erdp = false; + + /* DbC state machine: */ + switch (dbc->state) { + case DS_DISABLED: + case DS_INITIALIZED: + + return EVT_ERR; + case DS_ENABLED: + portsc = readl(&dbc->regs->portsc); + if (portsc & DBC_PORTSC_CONN_STATUS) { + dbc->state = DS_CONNECTED; + xhci_info(xhci, "DbC connected\n"); + } + + return EVT_DONE; + case DS_CONNECTED: + ctrl = readl(&dbc->regs->control); + if (ctrl & DBC_CTRL_DBC_RUN) { + dbc->state = DS_CONFIGURED; + xhci_info(xhci, "DbC configured\n"); + portsc = readl(&dbc->regs->portsc); + writel(portsc, &dbc->regs->portsc); + return EVT_GSER; + } + + return EVT_DONE; + case DS_CONFIGURED: + /* Handle cable unplug event: */ + portsc = readl(&dbc->regs->portsc); + if (!(portsc & DBC_PORTSC_PORT_ENABLED) && + !(portsc & DBC_PORTSC_CONN_STATUS)) { + xhci_info(xhci, "DbC cable unplugged\n"); + dbc->state = DS_ENABLED; + xhci_dbc_flush_reqests(dbc); + + return EVT_DISC; + } + + /* Handle debug port reset event: */ + if (portsc & DBC_PORTSC_RESET_CHANGE) { + xhci_info(xhci, "DbC port reset\n"); + writel(portsc, &dbc->regs->portsc); + dbc->state = DS_ENABLED; + xhci_dbc_flush_reqests(dbc); + + return EVT_DISC; + } + + /* Handle endpoint stall event: */ + ctrl = readl(&dbc->regs->control); + if ((ctrl & DBC_CTRL_HALT_IN_TR) || + (ctrl & DBC_CTRL_HALT_OUT_TR)) { + xhci_info(xhci, "DbC Endpoint stall\n"); + dbc->state = DS_STALLED; + + if (ctrl & DBC_CTRL_HALT_IN_TR) { + dep = get_in_ep(xhci); + xhci_dbc_flush_endpoint_requests(dep); + } + + if (ctrl & DBC_CTRL_HALT_OUT_TR) { + dep = get_out_ep(xhci); + xhci_dbc_flush_endpoint_requests(dep); + } + + return EVT_DONE; + } + + /* Clear DbC run change bit: */ + if (ctrl & DBC_CTRL_DBC_RUN_CHANGE) { + writel(ctrl, &dbc->regs->control); + ctrl = readl(&dbc->regs->control); + } + + break; + case DS_STALLED: + ctrl = readl(&dbc->regs->control); + if (!(ctrl & DBC_CTRL_HALT_IN_TR) && + !(ctrl & DBC_CTRL_HALT_OUT_TR) && + (ctrl & DBC_CTRL_DBC_RUN)) { + dbc->state = DS_CONFIGURED; + break; + } + + return EVT_DONE; + default: + xhci_err(xhci, "Unknown DbC state %d\n", dbc->state); + break; + } + + /* Handle the events in the event ring: */ + evt = dbc->ring_evt->dequeue; + while ((le32_to_cpu(evt->event_cmd.flags) & TRB_CYCLE) == + dbc->ring_evt->cycle_state) { + /* + * Add a barrier between reading the cycle flag and any + * reads of the event's flags/data below: + */ + rmb(); + + trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic); + + switch (le32_to_cpu(evt->event_cmd.flags) & TRB_TYPE_BITMASK) { + case TRB_TYPE(TRB_PORT_STATUS): + dbc_handle_port_status(xhci, evt); + break; + case TRB_TYPE(TRB_TRANSFER): + dbc_handle_xfer_event(xhci, evt); + break; + default: + break; + } + + inc_deq(xhci, dbc->ring_evt); + evt = dbc->ring_evt->dequeue; + update_erdp = true; + } + + /* Update event ring dequeue pointer: */ + if (update_erdp) { + deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg, + dbc->ring_evt->dequeue); + xhci_write_64(xhci, deq, &dbc->regs->erdp); + } + + return EVT_DONE; +} + +static void xhci_dbc_handle_events(struct work_struct *work) +{ + int ret; + enum evtreturn evtr; + struct xhci_dbc *dbc; + struct xhci_hcd *xhci; + + dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work); + xhci = dbc->xhci; + + spin_lock(&dbc->lock); + evtr = xhci_dbc_do_handle_events(dbc); + spin_unlock(&dbc->lock); + + switch (evtr) { + case EVT_GSER: + ret = xhci_dbc_tty_register_device(xhci); + if (ret) { + xhci_err(xhci, "failed to alloc tty device\n"); + break; + } + + xhci_info(xhci, "DbC now attached to /dev/ttyDBC0\n"); + break; + case EVT_DISC: + xhci_dbc_tty_unregister_device(xhci); + break; + case EVT_DONE: + break; + default: + xhci_info(xhci, "stop handling dbc events\n"); + return; + } + + mod_delayed_work(system_wq, &dbc->event_work, 1); +} + +static void xhci_do_dbc_exit(struct xhci_hcd *xhci) +{ + unsigned long flags; + + spin_lock_irqsave(&xhci->lock, flags); + kfree(xhci->dbc); + xhci->dbc = NULL; + spin_unlock_irqrestore(&xhci->lock, flags); +} + +static int xhci_do_dbc_init(struct xhci_hcd *xhci) +{ + u32 reg; + struct xhci_dbc *dbc; + unsigned long flags; + void __iomem *base; + int dbc_cap_offs; + + base = &xhci->cap_regs->hc_capbase; + dbc_cap_offs = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_DEBUG); + if (!dbc_cap_offs) + return -ENODEV; + + dbc = kzalloc(sizeof(*dbc), GFP_KERNEL); + if (!dbc) + return -ENOMEM; + + dbc->regs = base + dbc_cap_offs; + + /* We will avoid using DbC in xhci driver if it's in use. */ + reg = readl(&dbc->regs->control); + if (reg & DBC_CTRL_DBC_ENABLE) { + kfree(dbc); + return -EBUSY; + } + + spin_lock_irqsave(&xhci->lock, flags); + if (xhci->dbc) { + spin_unlock_irqrestore(&xhci->lock, flags); + kfree(dbc); + return -EBUSY; + } + xhci->dbc = dbc; + spin_unlock_irqrestore(&xhci->lock, flags); + + dbc->xhci = xhci; + INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events); + spin_lock_init(&dbc->lock); + + return 0; +} + +static ssize_t dbc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const char *p; + struct xhci_dbc *dbc; + struct xhci_hcd *xhci; + + xhci = hcd_to_xhci(dev_get_drvdata(dev)); + dbc = xhci->dbc; + + switch (dbc->state) { + case DS_DISABLED: + p = "disabled"; + break; + case DS_INITIALIZED: + p = "initialized"; + break; + case DS_ENABLED: + p = "enabled"; + break; + case DS_CONNECTED: + p = "connected"; + break; + case DS_CONFIGURED: + p = "configured"; + break; + case DS_STALLED: + p = "stalled"; + break; + default: + p = "unknown"; + } + + return sprintf(buf, "%s\n", p); +} + +static ssize_t dbc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct xhci_dbc *dbc; + struct xhci_hcd *xhci; + + xhci = hcd_to_xhci(dev_get_drvdata(dev)); + dbc = xhci->dbc; + + if (!strncmp(buf, "enable", 6)) + xhci_dbc_start(xhci); + else if (!strncmp(buf, "disable", 7)) + xhci_dbc_stop(xhci); + else + return -EINVAL; + + return count; +} + +static DEVICE_ATTR(dbc, 0644, dbc_show, dbc_store); + +int xhci_dbc_init(struct xhci_hcd *xhci) +{ + int ret; + struct device *dev = xhci_to_hcd(xhci)->self.controller; + + ret = xhci_do_dbc_init(xhci); + if (ret) + goto init_err3; + + ret = xhci_dbc_tty_register_driver(xhci); + if (ret) + goto init_err2; + + ret = device_create_file(dev, &dev_attr_dbc); + if (ret) + goto init_err1; + + return 0; + +init_err1: + xhci_dbc_tty_unregister_driver(); +init_err2: + xhci_do_dbc_exit(xhci); +init_err3: + return ret; +} + +void xhci_dbc_exit(struct xhci_hcd *xhci) +{ + struct device *dev = xhci_to_hcd(xhci)->self.controller; + + if (!xhci->dbc) + return; + + device_remove_file(dev, &dev_attr_dbc); + xhci_dbc_tty_unregister_driver(); + xhci_dbc_stop(xhci); + xhci_do_dbc_exit(xhci); +} + +#ifdef CONFIG_PM +int xhci_dbc_suspend(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + if (!dbc) + return 0; + + if (dbc->state == DS_CONFIGURED) + dbc->resume_required = 1; + + xhci_dbc_stop(xhci); + + return 0; +} + +int xhci_dbc_resume(struct xhci_hcd *xhci) +{ + int ret = 0; + struct xhci_dbc *dbc = xhci->dbc; + + if (!dbc) + return 0; + + if (dbc->resume_required) { + dbc->resume_required = 0; + xhci_dbc_start(xhci); + } + + return ret; +} +#endif /* CONFIG_PM */ diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h new file mode 100644 index 000000000000..e66ea0748ba3 --- /dev/null +++ b/drivers/usb/host/xhci-dbgcap.h @@ -0,0 +1,229 @@ + +/** + * xhci-dbgcap.h - xHCI debug capability support + * + * Copyright (C) 2017 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + */ +#ifndef __LINUX_XHCI_DBGCAP_H +#define __LINUX_XHCI_DBGCAP_H + +#include <linux/tty.h> +#include <linux/kfifo.h> + +struct dbc_regs { + __le32 capability; + __le32 doorbell; + __le32 ersts; /* Event Ring Segment Table Size*/ + __le32 __reserved_0; /* 0c~0f reserved bits */ + __le64 erstba; /* Event Ring Segment Table Base Address */ + __le64 erdp; /* Event Ring Dequeue Pointer */ + __le32 control; + __le32 status; + __le32 portsc; /* Port status and control */ + __le32 __reserved_1; /* 2b~28 reserved bits */ + __le64 dccp; /* Debug Capability Context Pointer */ + __le32 devinfo1; /* Device Descriptor Info Register 1 */ + __le32 devinfo2; /* Device Descriptor Info Register 2 */ +}; + +struct dbc_info_context { + __le64 string0; + __le64 manufacturer; + __le64 product; + __le64 serial; + __le32 length; + __le32 __reserved_0[7]; +}; + +#define DBC_CTRL_DBC_RUN BIT(0) +#define DBC_CTRL_PORT_ENABLE BIT(1) +#define DBC_CTRL_HALT_OUT_TR BIT(2) +#define DBC_CTRL_HALT_IN_TR BIT(3) +#define DBC_CTRL_DBC_RUN_CHANGE BIT(4) +#define DBC_CTRL_DBC_ENABLE BIT(31) +#define DBC_CTRL_MAXBURST(p) (((p) >> 16) & 0xff) +#define DBC_DOOR_BELL_TARGET(p) (((p) & 0xff) << 8) + +#define DBC_MAX_PACKET 1024 +#define DBC_MAX_STRING_LENGTH 64 +#define DBC_STRING_MANUFACTURER "Linux Foundation" +#define DBC_STRING_PRODUCT "Linux USB Debug Target" +#define DBC_STRING_SERIAL "0001" +#define DBC_CONTEXT_SIZE 64 + +/* + * Port status: + */ +#define DBC_PORTSC_CONN_STATUS BIT(0) +#define DBC_PORTSC_PORT_ENABLED BIT(1) +#define DBC_PORTSC_CONN_CHANGE BIT(17) +#define DBC_PORTSC_RESET_CHANGE BIT(21) +#define DBC_PORTSC_LINK_CHANGE BIT(22) +#define DBC_PORTSC_CONFIG_CHANGE BIT(23) + +struct dbc_str_descs { + char string0[DBC_MAX_STRING_LENGTH]; + char manufacturer[DBC_MAX_STRING_LENGTH]; + char product[DBC_MAX_STRING_LENGTH]; + char serial[DBC_MAX_STRING_LENGTH]; +}; + +#define DBC_PROTOCOL 1 /* GNU Remote Debug Command */ +#define DBC_VENDOR_ID 0x1d6b /* Linux Foundation 0x1d6b */ +#define DBC_PRODUCT_ID 0x0010 /* device 0010 */ +#define DBC_DEVICE_REV 0x0010 /* 0.10 */ + +enum dbc_state { + DS_DISABLED = 0, + DS_INITIALIZED, + DS_ENABLED, + DS_CONNECTED, + DS_CONFIGURED, + DS_STALLED, +}; + +struct dbc_request { + void *buf; + unsigned int length; + dma_addr_t dma; + void (*complete)(struct xhci_hcd *xhci, + struct dbc_request *req); + struct list_head list_pool; + int status; + unsigned int actual; + + struct dbc_ep *dep; + struct list_head list_pending; + dma_addr_t trb_dma; + union xhci_trb *trb; + unsigned direction:1; +}; + +struct dbc_ep { + struct xhci_dbc *dbc; + struct list_head list_pending; + struct xhci_ring *ring; + unsigned direction:1; +}; + +#define DBC_QUEUE_SIZE 16 +#define DBC_WRITE_BUF_SIZE 8192 + +/* + * Private structure for DbC hardware state: + */ +struct dbc_port { + struct tty_port port; + spinlock_t port_lock; /* port access */ + + struct list_head read_pool; + struct list_head read_queue; + unsigned int n_read; + struct tasklet_struct push; + + struct list_head write_pool; + struct kfifo write_fifo; + + bool registered; + struct dbc_ep *in; + struct dbc_ep *out; +}; + +struct xhci_dbc { + spinlock_t lock; /* device access */ + struct xhci_hcd *xhci; + struct dbc_regs __iomem *regs; + struct xhci_ring *ring_evt; + struct xhci_ring *ring_in; + struct xhci_ring *ring_out; + struct xhci_erst erst; + struct xhci_container_ctx *ctx; + + struct dbc_str_descs *string; + dma_addr_t string_dma; + size_t string_size; + + enum dbc_state state; + struct delayed_work event_work; + unsigned resume_required:1; + struct dbc_ep eps[2]; + + struct dbc_port port; +}; + +#define dbc_bulkout_ctx(d) \ + ((struct xhci_ep_ctx *)((d)->ctx->bytes + DBC_CONTEXT_SIZE)) +#define dbc_bulkin_ctx(d) \ + ((struct xhci_ep_ctx *)((d)->ctx->bytes + DBC_CONTEXT_SIZE * 2)) +#define dbc_bulkout_enq(d) \ + xhci_trb_virt_to_dma((d)->ring_out->enq_seg, (d)->ring_out->enqueue) +#define dbc_bulkin_enq(d) \ + xhci_trb_virt_to_dma((d)->ring_in->enq_seg, (d)->ring_in->enqueue) +#define dbc_epctx_info2(t, p, b) \ + cpu_to_le32(EP_TYPE(t) | MAX_PACKET(p) | MAX_BURST(b)) +#define dbc_ep_dma_direction(d) \ + ((d)->direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE) + +#define BULK_OUT 0 +#define BULK_IN 1 +#define EPID_OUT 2 +#define EPID_IN 3 + +enum evtreturn { + EVT_ERR = -1, + EVT_DONE, + EVT_GSER, + EVT_DISC, +}; + +static inline struct dbc_ep *get_in_ep(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + return &dbc->eps[BULK_IN]; +} + +static inline struct dbc_ep *get_out_ep(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + return &dbc->eps[BULK_OUT]; +} + +#ifdef CONFIG_USB_XHCI_DBGCAP +int xhci_dbc_init(struct xhci_hcd *xhci); +void xhci_dbc_exit(struct xhci_hcd *xhci); +int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci); +void xhci_dbc_tty_unregister_driver(void); +int xhci_dbc_tty_register_device(struct xhci_hcd *xhci); +void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci); +struct dbc_request *dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags); +void dbc_free_request(struct dbc_ep *dep, struct dbc_request *req); +int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req, gfp_t gfp_flags); +#ifdef CONFIG_PM +int xhci_dbc_suspend(struct xhci_hcd *xhci); +int xhci_dbc_resume(struct xhci_hcd *xhci); +#endif /* CONFIG_PM */ +#else +static inline int xhci_dbc_init(struct xhci_hcd *xhci) +{ + return 0; +} + +static inline void xhci_dbc_exit(struct xhci_hcd *xhci) +{ +} + +static inline int xhci_dbc_suspend(struct xhci_hcd *xhci) +{ + return 0; +} + +static inline int xhci_dbc_resume(struct xhci_hcd *xhci) +{ + return 0; +} +#endif /* CONFIG_USB_XHCI_DBGCAP */ +#endif /* __LINUX_XHCI_DBGCAP_H */ diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c new file mode 100644 index 000000000000..8d47b6fbf973 --- /dev/null +++ b/drivers/usb/host/xhci-dbgtty.c @@ -0,0 +1,497 @@ +/** + * xhci-dbgtty.c - tty glue for xHCI debug capability + * + * Copyright (C) 2017 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + */ + +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +#include "xhci.h" +#include "xhci-dbgcap.h" + +static unsigned int +dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size) +{ + unsigned int len; + + len = kfifo_len(&port->write_fifo); + if (len < size) + size = len; + if (size != 0) + size = kfifo_out(&port->write_fifo, packet, size); + return size; +} + +static int dbc_start_tx(struct dbc_port *port) + __releases(&port->port_lock) + __acquires(&port->port_lock) +{ + int len; + struct dbc_request *req; + int status = 0; + bool do_tty_wake = false; + struct list_head *pool = &port->write_pool; + + while (!list_empty(pool)) { + req = list_entry(pool->next, struct dbc_request, list_pool); + len = dbc_send_packet(port, req->buf, DBC_MAX_PACKET); + if (len == 0) + break; + do_tty_wake = true; + + req->length = len; + list_del(&req->list_pool); + + spin_unlock(&port->port_lock); + status = dbc_ep_queue(port->out, req, GFP_ATOMIC); + spin_lock(&port->port_lock); + + if (status) { + list_add(&req->list_pool, pool); + break; + } + } + + if (do_tty_wake && port->port.tty) + tty_wakeup(port->port.tty); + + return status; +} + +static void dbc_start_rx(struct dbc_port *port) + __releases(&port->port_lock) + __acquires(&port->port_lock) +{ + struct dbc_request *req; + int status; + struct list_head *pool = &port->read_pool; + + while (!list_empty(pool)) { + if (!port->port.tty) + break; + + req = list_entry(pool->next, struct dbc_request, list_pool); + list_del(&req->list_pool); + req->length = DBC_MAX_PACKET; + + spin_unlock(&port->port_lock); + status = dbc_ep_queue(port->in, req, GFP_ATOMIC); + spin_lock(&port->port_lock); + + if (status) { + list_add(&req->list_pool, pool); + break; + } + } +} + +static void +dbc_read_complete(struct xhci_hcd *xhci, struct dbc_request *req) +{ + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + spin_lock(&port->port_lock); + list_add_tail(&req->list_pool, &port->read_queue); + tasklet_schedule(&port->push); + spin_unlock(&port->port_lock); +} + +static void dbc_write_complete(struct xhci_hcd *xhci, struct dbc_request *req) +{ + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + spin_lock(&port->port_lock); + list_add(&req->list_pool, &port->write_pool); + switch (req->status) { + case 0: + dbc_start_tx(port); + break; + case -ESHUTDOWN: + break; + default: + xhci_warn(xhci, "unexpected write complete status %d\n", + req->status); + break; + } + spin_unlock(&port->port_lock); +} + +static void xhci_dbc_free_req(struct dbc_ep *dep, struct dbc_request *req) +{ + kfree(req->buf); + dbc_free_request(dep, req); +} + +static int +xhci_dbc_alloc_requests(struct dbc_ep *dep, struct list_head *head, + void (*fn)(struct xhci_hcd *, struct dbc_request *)) +{ + int i; + struct dbc_request *req; + + for (i = 0; i < DBC_QUEUE_SIZE; i++) { + req = dbc_alloc_request(dep, GFP_ATOMIC); + if (!req) + break; + + req->length = DBC_MAX_PACKET; + req->buf = kmalloc(req->length, GFP_KERNEL); + if (!req->buf) { + xhci_dbc_free_req(dep, req); + break; + } + + req->complete = fn; + list_add_tail(&req->list_pool, head); + } + + return list_empty(head) ? -ENOMEM : 0; +} + +static void +xhci_dbc_free_requests(struct dbc_ep *dep, struct list_head *head) +{ + struct dbc_request *req; + + while (!list_empty(head)) { + req = list_entry(head->next, struct dbc_request, list_pool); + list_del(&req->list_pool); + xhci_dbc_free_req(dep, req); + } +} + +static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct dbc_port *port = driver->driver_state; + + tty->driver_data = port; + + return tty_port_install(&port->port, driver, tty); +} + +static int dbc_tty_open(struct tty_struct *tty, struct file *file) +{ + struct dbc_port *port = tty->driver_data; + + return tty_port_open(&port->port, tty, file); +} + +static void dbc_tty_close(struct tty_struct *tty, struct file *file) +{ + struct dbc_port *port = tty->driver_data; + + tty_port_close(&port->port, tty, file); +} + +static int dbc_tty_write(struct tty_struct *tty, + const unsigned char *buf, + int count) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + if (count) + count = kfifo_in(&port->write_fifo, buf, count); + dbc_start_tx(port); + spin_unlock_irqrestore(&port->port_lock, flags); + + return count; +} + +static int dbc_tty_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + int status; + + spin_lock_irqsave(&port->port_lock, flags); + status = kfifo_put(&port->write_fifo, ch); + spin_unlock_irqrestore(&port->port_lock, flags); + + return status; +} + +static void dbc_tty_flush_chars(struct tty_struct *tty) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + dbc_start_tx(port); + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static int dbc_tty_write_room(struct tty_struct *tty) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + int room = 0; + + spin_lock_irqsave(&port->port_lock, flags); + room = kfifo_avail(&port->write_fifo); + spin_unlock_irqrestore(&port->port_lock, flags); + + return room; +} + +static int dbc_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + int chars = 0; + + spin_lock_irqsave(&port->port_lock, flags); + chars = kfifo_len(&port->write_fifo); + spin_unlock_irqrestore(&port->port_lock, flags); + + return chars; +} + +static void dbc_tty_unthrottle(struct tty_struct *tty) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + tasklet_schedule(&port->push); + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static const struct tty_operations dbc_tty_ops = { + .install = dbc_tty_install, + .open = dbc_tty_open, + .close = dbc_tty_close, + .write = dbc_tty_write, + .put_char = dbc_tty_put_char, + .flush_chars = dbc_tty_flush_chars, + .write_room = dbc_tty_write_room, + .chars_in_buffer = dbc_tty_chars_in_buffer, + .unthrottle = dbc_tty_unthrottle, +}; + +static struct tty_driver *dbc_tty_driver; + +int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci) +{ + int status; + struct xhci_dbc *dbc = xhci->dbc; + + dbc_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(dbc_tty_driver)) { + status = PTR_ERR(dbc_tty_driver); + dbc_tty_driver = NULL; + return status; + } + + dbc_tty_driver->driver_name = "dbc_serial"; + dbc_tty_driver->name = "ttyDBC"; + + dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL; + dbc_tty_driver->init_termios = tty_std_termios; + dbc_tty_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + dbc_tty_driver->init_termios.c_ispeed = 9600; + dbc_tty_driver->init_termios.c_ospeed = 9600; + dbc_tty_driver->driver_state = &dbc->port; + + tty_set_operations(dbc_tty_driver, &dbc_tty_ops); + + status = tty_register_driver(dbc_tty_driver); + if (status) { + xhci_err(xhci, + "can't register dbc tty driver, err %d\n", status); + put_tty_driver(dbc_tty_driver); + dbc_tty_driver = NULL; + } + + return status; +} + +void xhci_dbc_tty_unregister_driver(void) +{ + tty_unregister_driver(dbc_tty_driver); + put_tty_driver(dbc_tty_driver); + dbc_tty_driver = NULL; +} + +static void dbc_rx_push(unsigned long _port) +{ + struct dbc_request *req; + struct tty_struct *tty; + bool do_push = false; + bool disconnect = false; + struct dbc_port *port = (void *)_port; + struct list_head *queue = &port->read_queue; + + spin_lock_irq(&port->port_lock); + tty = port->port.tty; + while (!list_empty(queue)) { + req = list_first_entry(queue, struct dbc_request, list_pool); + + if (tty && tty_throttled(tty)) + break; + + switch (req->status) { + case 0: + break; + case -ESHUTDOWN: + disconnect = true; + break; + default: + pr_warn("ttyDBC0: unexpected RX status %d\n", + req->status); + break; + } + + if (req->actual) { + char *packet = req->buf; + unsigned int n, size = req->actual; + int count; + + n = port->n_read; + if (n) { + packet += n; + size -= n; + } + + count = tty_insert_flip_string(&port->port, packet, + size); + if (count) + do_push = true; + if (count != size) { + port->n_read += count; + break; + } + port->n_read = 0; + } + + list_move(&req->list_pool, &port->read_pool); + } + + if (do_push) + tty_flip_buffer_push(&port->port); + + if (!list_empty(queue) && tty) { + if (!tty_throttled(tty)) { + if (do_push) + tasklet_schedule(&port->push); + else + pr_warn("ttyDBC0: RX not scheduled?\n"); + } + } + + if (!disconnect) + dbc_start_rx(port); + + spin_unlock_irq(&port->port_lock); +} + +static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty) +{ + struct dbc_port *port = container_of(_port, struct dbc_port, port); + + spin_lock_irq(&port->port_lock); + dbc_start_rx(port); + spin_unlock_irq(&port->port_lock); + + return 0; +} + +static const struct tty_port_operations dbc_port_ops = { + .activate = dbc_port_activate, +}; + +static void +xhci_dbc_tty_init_port(struct xhci_hcd *xhci, struct dbc_port *port) +{ + tty_port_init(&port->port); + spin_lock_init(&port->port_lock); + tasklet_init(&port->push, dbc_rx_push, (unsigned long)port); + INIT_LIST_HEAD(&port->read_pool); + INIT_LIST_HEAD(&port->read_queue); + INIT_LIST_HEAD(&port->write_pool); + + port->in = get_in_ep(xhci); + port->out = get_out_ep(xhci); + port->port.ops = &dbc_port_ops; + port->n_read = 0; +} + +static void +xhci_dbc_tty_exit_port(struct dbc_port *port) +{ + tasklet_kill(&port->push); + tty_port_destroy(&port->port); +} + +int xhci_dbc_tty_register_device(struct xhci_hcd *xhci) +{ + int ret; + struct device *tty_dev; + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + xhci_dbc_tty_init_port(xhci, port); + tty_dev = tty_port_register_device(&port->port, + dbc_tty_driver, 0, NULL); + ret = IS_ERR_OR_NULL(tty_dev); + if (ret) + goto register_fail; + + ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL); + if (ret) + goto buf_alloc_fail; + + ret = xhci_dbc_alloc_requests(port->in, &port->read_pool, + dbc_read_complete); + if (ret) + goto request_fail; + + ret = xhci_dbc_alloc_requests(port->out, &port->write_pool, + dbc_write_complete); + if (ret) + goto request_fail; + + port->registered = true; + + return 0; + +request_fail: + xhci_dbc_free_requests(port->in, &port->read_pool); + xhci_dbc_free_requests(port->out, &port->write_pool); + kfifo_free(&port->write_fifo); + +buf_alloc_fail: + tty_unregister_device(dbc_tty_driver, 0); + +register_fail: + xhci_dbc_tty_exit_port(port); + + xhci_err(xhci, "can't register tty port, err %d\n", ret); + + return ret; +} + +void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + tty_unregister_device(dbc_tty_driver, 0); + xhci_dbc_tty_exit_port(port); + port->registered = false; + + kfifo_free(&port->write_fifo); + xhci_dbc_free_requests(get_out_ep(xhci), &port->read_pool); + xhci_dbc_free_requests(get_out_ep(xhci), &port->read_queue); + xhci_dbc_free_requests(get_in_ep(xhci), &port->write_pool); +} diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 4f7895dbcf88..e26e685d8a57 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -162,7 +162,7 @@ static void xhci_debugfs_extcap_regset(struct xhci_hcd *xhci, int cap_id, static int xhci_ring_enqueue_show(struct seq_file *s, void *unused) { dma_addr_t dma; - struct xhci_ring *ring = s->private; + struct xhci_ring *ring = *(struct xhci_ring **)s->private; dma = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); seq_printf(s, "%pad\n", &dma); @@ -173,7 +173,7 @@ static int xhci_ring_enqueue_show(struct seq_file *s, void *unused) static int xhci_ring_dequeue_show(struct seq_file *s, void *unused) { dma_addr_t dma; - struct xhci_ring *ring = s->private; + struct xhci_ring *ring = *(struct xhci_ring **)s->private; dma = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue); seq_printf(s, "%pad\n", &dma); @@ -183,7 +183,7 @@ static int xhci_ring_dequeue_show(struct seq_file *s, void *unused) static int xhci_ring_cycle_show(struct seq_file *s, void *unused) { - struct xhci_ring *ring = s->private; + struct xhci_ring *ring = *(struct xhci_ring **)s->private; seq_printf(s, "%d\n", ring->cycle_state); @@ -346,7 +346,7 @@ static void xhci_debugfs_create_files(struct xhci_hcd *xhci, } static struct dentry *xhci_debugfs_create_ring_dir(struct xhci_hcd *xhci, - struct xhci_ring *ring, + struct xhci_ring **ring, const char *name, struct dentry *parent) { @@ -387,7 +387,7 @@ void xhci_debugfs_create_endpoint(struct xhci_hcd *xhci, snprintf(epriv->name, sizeof(epriv->name), "ep%02d", ep_index); epriv->root = xhci_debugfs_create_ring_dir(xhci, - dev->eps[ep_index].new_ring, + &dev->eps[ep_index].new_ring, epriv->name, spriv->root); spriv->eps[ep_index] = epriv; @@ -423,7 +423,7 @@ void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id) priv->dev = dev; dev->debugfs_private = priv; - xhci_debugfs_create_ring_dir(xhci, dev->eps[0].ring, + xhci_debugfs_create_ring_dir(xhci, &dev->eps[0].ring, "ep00", priv->root); xhci_debugfs_create_context_files(xhci, priv->root, slot_id); @@ -488,11 +488,11 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) ARRAY_SIZE(xhci_extcap_dbc), "reg-ext-dbc"); - xhci_debugfs_create_ring_dir(xhci, xhci->cmd_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->cmd_ring, "command-ring", xhci->debugfs_root); - xhci_debugfs_create_ring_dir(xhci, xhci->event_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->event_ring, "event-ring", xhci->debugfs_root); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 2a90229be7a6..46d5e08f05f1 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -388,7 +388,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) trace_xhci_stop_device(virt_dev); - cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO); + cmd = xhci_alloc_command(xhci, true, GFP_NOIO); if (!cmd) return -ENOMEM; @@ -404,8 +404,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) continue; - command = xhci_alloc_command(xhci, false, false, - GFP_NOWAIT); + command = xhci_alloc_command(xhci, false, GFP_NOWAIT); if (!command) { spin_unlock_irqrestore(&xhci->lock, flags); ret = -ENOMEM; @@ -1077,6 +1076,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, retval = -ENODEV; break; } + trace_xhci_get_port_status(wIndex, temp); status = xhci_get_port_status(hcd, bus_state, port_array, wIndex, temp, flags); if (status == 0xffffffff) @@ -1443,6 +1443,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) retval = -ENODEV; break; } + trace_xhci_hub_status_data(i, temp); + if ((temp & mask) != 0 || (bus_state->port_c_suspend & 1 << i) || (bus_state->resume_done[i] && time_after_eq( diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index e1fba4688509..332420d10be9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -357,7 +357,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, * Set the end flag and the cycle toggle bit on the last segment. * See section 4.9.1 and figures 15 and 16. */ -static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, unsigned int cycle_state, enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) { @@ -454,7 +454,7 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, return 0; } -static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, +struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type, gfp_t flags) { struct xhci_container_ctx *ctx; @@ -479,7 +479,7 @@ static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci return ctx; } -static void xhci_free_container_ctx(struct xhci_hcd *xhci, +void xhci_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx) { if (!ctx) @@ -650,7 +650,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, /* Allocate everything needed to free the stream rings later */ stream_info->free_streams_command = - xhci_alloc_command(xhci, true, true, mem_flags); + xhci_alloc_command_with_ctx(xhci, true, mem_flags); if (!stream_info->free_streams_command) goto cleanup_ctx; @@ -934,6 +934,12 @@ void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) if (!vdev) return; + if (vdev->real_port == 0 || + vdev->real_port > HCS_MAX_PORTS(xhci->hcs_params1)) { + xhci_dbg(xhci, "Bad vdev->real_port.\n"); + goto out; + } + tt_list_head = &(xhci->rh_bw[vdev->real_port - 1].tts); list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) { /* is this a hub device that added a tt_info to the tts list */ @@ -947,6 +953,7 @@ void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) } } } +out: /* we are now at a leaf device */ xhci_debugfs_remove_slot(xhci, slot_id); xhci_free_virt_device(xhci, slot_id); @@ -964,10 +971,9 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, return 0; } - xhci->devs[slot_id] = kzalloc(sizeof(*xhci->devs[slot_id]), flags); - if (!xhci->devs[slot_id]) + dev = kzalloc(sizeof(*dev), flags); + if (!dev) return 0; - dev = xhci->devs[slot_id]; /* Allocate the (output) device context that will be used in the HC. */ dev->out_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags); @@ -1008,9 +1014,17 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, trace_xhci_alloc_virt_device(dev); + xhci->devs[slot_id] = dev; + return 1; fail: - xhci_free_virt_device(xhci, slot_id); + + if (dev->in_ctx) + xhci_free_container_ctx(xhci, dev->in_ctx); + if (dev->out_ctx) + xhci_free_container_ctx(xhci, dev->out_ctx); + kfree(dev); + return 0; } @@ -1701,8 +1715,7 @@ static void scratchpad_free(struct xhci_hcd *xhci) } struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, - bool allocate_in_ctx, bool allocate_completion, - gfp_t mem_flags) + bool allocate_completion, gfp_t mem_flags) { struct xhci_command *command; @@ -1710,21 +1723,10 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, if (!command) return NULL; - if (allocate_in_ctx) { - command->in_ctx = - xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, - mem_flags); - if (!command->in_ctx) { - kfree(command); - return NULL; - } - } - if (allocate_completion) { command->completion = kzalloc(sizeof(struct completion), mem_flags); if (!command->completion) { - xhci_free_container_ctx(xhci, command->in_ctx); kfree(command); return NULL; } @@ -1736,6 +1738,25 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, return command; } +struct xhci_command *xhci_alloc_command_with_ctx(struct xhci_hcd *xhci, + bool allocate_completion, gfp_t mem_flags) +{ + struct xhci_command *command; + + command = xhci_alloc_command(xhci, allocate_completion, mem_flags); + if (!command) + return NULL; + + command->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, + mem_flags); + if (!command->in_ctx) { + kfree(command->completion); + kfree(command); + return NULL; + } + return command; +} + void xhci_urb_free_priv(struct urb_priv *urb_priv) { kfree(urb_priv); @@ -1750,21 +1771,58 @@ void xhci_free_command(struct xhci_hcd *xhci, kfree(command); } +int xhci_alloc_erst(struct xhci_hcd *xhci, + struct xhci_ring *evt_ring, + struct xhci_erst *erst, + gfp_t flags) +{ + size_t size; + unsigned int val; + struct xhci_segment *seg; + struct xhci_erst_entry *entry; + + size = sizeof(struct xhci_erst_entry) * evt_ring->num_segs; + erst->entries = dma_zalloc_coherent(xhci_to_hcd(xhci)->self.sysdev, + size, &erst->erst_dma_addr, flags); + if (!erst->entries) + return -ENOMEM; + + erst->num_entries = evt_ring->num_segs; + + seg = evt_ring->first_seg; + for (val = 0; val < evt_ring->num_segs; val++) { + entry = &erst->entries[val]; + entry->seg_addr = cpu_to_le64(seg->dma); + entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); + entry->rsvd = 0; + seg = seg->next; + } + + return 0; +} + +void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) +{ + size_t size; + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + + size = sizeof(struct xhci_erst_entry) * (erst->num_entries); + if (erst->entries) + dma_free_coherent(dev, size, + erst->entries, + erst->erst_dma_addr); + erst->entries = NULL; +} + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - int size; int i, j, num_ports; cancel_delayed_work_sync(&xhci->cmd_timer); - /* Free the Event Ring Segment Table and the actual Event Ring */ - size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); - if (xhci->erst.entries) - dma_free_coherent(dev, size, - xhci->erst.entries, xhci->erst.erst_dma_addr); - xhci->erst.entries = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed ERST"); + xhci_free_erst(xhci, &xhci->erst); + if (xhci->event_ring) xhci_ring_free(xhci, xhci->event_ring); xhci->event_ring = NULL; @@ -2301,9 +2359,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) struct device *dev = xhci_to_hcd(xhci)->self.sysdev; unsigned int val, val2; u64 val_64; - struct xhci_segment *seg; - u32 page_size, temp; - int i; + u32 page_size, temp; + int i, ret; INIT_LIST_HEAD(&xhci->cmd_list); @@ -2407,9 +2464,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Setting command ring address to 0x%016llx", val_64); xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); - xhci_dbg_cmd_ptrs(xhci); - xhci->lpm_command = xhci_alloc_command(xhci, true, true, flags); + xhci->lpm_command = xhci_alloc_command_with_ctx(xhci, true, flags); if (!xhci->lpm_command) goto fail; @@ -2425,8 +2481,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) "// 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); /* Set ir_set to interrupt register set 0 */ xhci->ir_set = &xhci->run_regs->ir_set[0]; @@ -2442,32 +2496,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) if (xhci_check_trb_in_td_math(xhci) < 0) goto fail; - xhci->erst.entries = dma_alloc_coherent(dev, - sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma, - flags); - if (!xhci->erst.entries) + ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); + if (ret) goto fail; - 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_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); - - /* set ring base address and size for each segment table entry */ - for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) { - struct xhci_erst_entry *entry = &xhci->erst.entries[val]; - entry->seg_addr = cpu_to_le64(seg->dma); - entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); - entry->rsvd = 0; - seg = seg->next; - } /* set ERST count with the number of entries in the segment table */ val = readl(&xhci->ir_set->erst_size); @@ -2493,7 +2524,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci_set_hc_event_deq(xhci); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Wrote ERST address to ir_set 0."); - xhci_print_ir_set(xhci, 0); /* * XXX: Might need to set the Interrupter Moderation Register to diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index b62a1d23244b..6da52df11228 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -57,26 +57,21 @@ /* u2_phy_pll register */ #define CTRL_U2_FORCE_PLL_STB BIT(28) -#define PERI_WK_CTRL0 0x400 -#define UWK_CTR0_0P_LS_PE BIT(8) /* posedge */ -#define UWK_CTR0_0P_LS_NE BIT(7) /* negedge for 0p linestate*/ -#define UWK_CTL1_1P_LS_C(x) (((x) & 0xf) << 1) -#define UWK_CTL1_1P_LS_E BIT(0) - -#define PERI_WK_CTRL1 0x404 -#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26) -#define UWK_CTL1_IS_E BIT(25) -#define UWK_CTL1_0P_LS_C(x) (((x) & 0xf) << 21) -#define UWK_CTL1_0P_LS_E BIT(20) -#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */ -#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */ -#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */ -#define UWK_CTL1_0P_LS_P BIT(7) -#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */ - -enum ssusb_wakeup_src { - SSUSB_WK_IP_SLEEP = 1, - SSUSB_WK_LINE_STATE = 2, +/* usb remote wakeup registers in syscon */ +/* mt8173 etc */ +#define PERI_WK_CTRL1 0x4 +#define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */ +#define WC1_IS_EN BIT(25) +#define WC1_IS_P BIT(6) /* polarity for ip sleep */ + +/* mt2712 etc */ +#define PERI_SSUSB_SPM_CTRL 0x0 +#define SSC_IP_SLEEP_EN BIT(4) +#define SSC_SPM_INT_EN BIT(1) + +enum ssusb_uwk_vers { + SSUSB_UWK_V1 = 1, + SSUSB_UWK_V2, }; static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk) @@ -296,112 +291,58 @@ static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk) } /* only clocks can be turn off for ip-sleep wakeup mode */ -static void usb_wakeup_ip_sleep_en(struct xhci_hcd_mtk *mtk) +static void usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable) { - u32 tmp; - struct regmap *pericfg = mtk->pericfg; - - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_IS_P; - tmp &= ~(UWK_CTL1_IS_C(0xf)); - tmp |= UWK_CTL1_IS_C(0x8); - regmap_write(pericfg, PERI_WK_CTRL1, tmp); - regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E); - - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - dev_dbg(mtk->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n", - __func__, tmp); + u32 reg, msk, val; + + switch (mtk->uwk_vers) { + case SSUSB_UWK_V1: + reg = mtk->uwk_reg_base + PERI_WK_CTRL1; + msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P; + val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0; + break; + case SSUSB_UWK_V2: + reg = mtk->uwk_reg_base + PERI_SSUSB_SPM_CTRL; + msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; + val = enable ? msk : 0; + break; + default: + return; + }; + regmap_update_bits(mtk->uwk, reg, msk, val); } -static void usb_wakeup_ip_sleep_dis(struct xhci_hcd_mtk *mtk) +static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk, + struct device_node *dn) { - u32 tmp; + struct of_phandle_args args; + int ret; - regmap_read(mtk->pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_IS_E; - regmap_write(mtk->pericfg, PERI_WK_CTRL1, tmp); -} + /* Wakeup function is optional */ + mtk->uwk_en = of_property_read_bool(dn, "wakeup-source"); + if (!mtk->uwk_en) + return 0; -/* -* for line-state wakeup mode, phy's power should not power-down -* and only support cable plug in/out -*/ -static void usb_wakeup_line_state_en(struct xhci_hcd_mtk *mtk) -{ - u32 tmp; - struct regmap *pericfg = mtk->pericfg; - - /* line-state of u2-port0 */ - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_0P_LS_P; - tmp &= ~(UWK_CTL1_0P_LS_C(0xf)); - tmp |= UWK_CTL1_0P_LS_C(0x8); - regmap_write(pericfg, PERI_WK_CTRL1, tmp); - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_0P_LS_E); - - /* line-state of u2-port1 */ - regmap_read(pericfg, PERI_WK_CTRL0, &tmp); - tmp &= ~(UWK_CTL1_1P_LS_C(0xf)); - tmp |= UWK_CTL1_1P_LS_C(0x8); - regmap_write(pericfg, PERI_WK_CTRL0, tmp); - regmap_write(pericfg, PERI_WK_CTRL0, tmp | UWK_CTL1_1P_LS_E); -} + ret = of_parse_phandle_with_fixed_args(dn, + "mediatek,syscon-wakeup", 2, 0, &args); + if (ret) + return ret; -static void usb_wakeup_line_state_dis(struct xhci_hcd_mtk *mtk) -{ - u32 tmp; - struct regmap *pericfg = mtk->pericfg; - - /* line-state of u2-port0 */ - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_0P_LS_E; - regmap_write(pericfg, PERI_WK_CTRL1, tmp); - - /* line-state of u2-port1 */ - regmap_read(pericfg, PERI_WK_CTRL0, &tmp); - tmp &= ~UWK_CTL1_1P_LS_E; - regmap_write(pericfg, PERI_WK_CTRL0, tmp); -} + mtk->uwk_reg_base = args.args[0]; + mtk->uwk_vers = args.args[1]; + mtk->uwk = syscon_node_to_regmap(args.np); + of_node_put(args.np); + dev_info(mtk->dev, "uwk - reg:0x%x, version:%d\n", + mtk->uwk_reg_base, mtk->uwk_vers); -static void usb_wakeup_enable(struct xhci_hcd_mtk *mtk) -{ - if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP) - usb_wakeup_ip_sleep_en(mtk); - else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE) - usb_wakeup_line_state_en(mtk); -} + return PTR_ERR_OR_ZERO(mtk->uwk); -static void usb_wakeup_disable(struct xhci_hcd_mtk *mtk) -{ - if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP) - usb_wakeup_ip_sleep_dis(mtk); - else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE) - usb_wakeup_line_state_dis(mtk); } -static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk, - struct device_node *dn) +static void usb_wakeup_set(struct xhci_hcd_mtk *mtk, bool enable) { - struct device *dev = mtk->dev; - - /* - * wakeup function is optional, so it is not an error if this property - * does not exist, and in such case, no need to get relative - * properties anymore. - */ - of_property_read_u32(dn, "mediatek,wakeup-src", &mtk->wakeup_src); - if (!mtk->wakeup_src) - return 0; - - mtk->pericfg = syscon_regmap_lookup_by_phandle(dn, - "mediatek,syscon-wakeup"); - if (IS_ERR(mtk->pericfg)) { - dev_err(dev, "fail to get pericfg regs\n"); - return PTR_ERR(mtk->pericfg); - } - - return 0; + if (mtk->uwk_en) + usb_wakeup_ip_sleep_set(mtk, enable); } static int xhci_mtk_setup(struct usb_hcd *hcd); @@ -583,8 +524,10 @@ static int xhci_mtk_probe(struct platform_device *pdev) &mtk->u3p_dis_msk); ret = usb_wakeup_of_property_parse(mtk, node); - if (ret) + if (ret) { + dev_err(dev, "failed to parse uwk property\n"); return ret; + } mtk->num_phys = of_count_phandle_with_args(node, "phys", "#phy-cells"); @@ -674,6 +617,15 @@ static int xhci_mtk_probe(struct platform_device *pdev) xhci = hcd_to_xhci(hcd); xhci->main_hcd = hcd; + + /* + * imod_interval is the interrupt moderation value in nanoseconds. + * The increment interval is 8 times as much as that defined in + * the xHCI spec on MTK's controller. + */ + xhci->imod_interval = 5000; + device_property_read_u32(dev, "imod-interval-ns", &xhci->imod_interval); + xhci->shared_hcd = usb_create_shared_hcd(driver, dev, dev_name(dev), hcd); if (!xhci->shared_hcd) { @@ -768,7 +720,7 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev) xhci_mtk_host_disable(mtk); xhci_mtk_phy_power_off(mtk); xhci_mtk_clks_disable(mtk); - usb_wakeup_enable(mtk); + usb_wakeup_set(mtk, true); return 0; } @@ -778,7 +730,7 @@ static int __maybe_unused xhci_mtk_resume(struct device *dev) struct usb_hcd *hcd = mtk->hcd; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - usb_wakeup_disable(mtk); + usb_wakeup_set(mtk, false); xhci_mtk_clks_enable(mtk); xhci_mtk_phy_power_on(mtk); xhci_mtk_host_enable(mtk); diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 6b74ce5b7564..cc59d80b663b 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -122,8 +122,12 @@ struct xhci_hcd_mtk { struct regmap *pericfg; struct phy **phys; int num_phys; - int wakeup_src; bool lpm_support; + /* usb remote wakeup */ + bool uwk_en; + struct regmap *uwk; + u32 uwk_reg_base; + u32 uwk_vers; }; static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 7ef1274ef7f7..6c79037876db 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -178,6 +178,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_BROKEN_STREAMS; } if (pdev->vendor == PCI_VENDOR_ID_RENESAS && + pdev->device == 0x0014) + xhci->quirks |= XHCI_TRUST_TX_LENGTH; + if (pdev->vendor == PCI_VENDOR_ID_RENESAS && pdev->device == 0x0015) xhci->quirks |= XHCI_RESET_ON_RESUME; if (pdev->vendor == PCI_VENDOR_ID_VIA) @@ -234,6 +237,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!xhci->sbrn) pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn); + /* imod_interval is the interrupt moderation value in nanoseconds. */ + xhci->imod_interval = 40000; + retval = xhci_gen_setup(hcd, xhci_pci_quirks); if (retval) return retval; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 09f164f8cf8c..6f038306c14d 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -269,6 +269,11 @@ static int xhci_plat_probe(struct platform_device *pdev) if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped")) xhci->quirks |= XHCI_BROKEN_PORT_PED; + /* imod_interval is the interrupt moderation value in nanoseconds. */ + xhci->imod_interval = 40000; + device_property_read_u32(sysdev, "imod-interval-ns", + &xhci->imod_interval); + hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0); if (IS_ERR(hcd->usb_phy)) { ret = PTR_ERR(hcd->usb_phy); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index c239c688076c..daa94c3aed80 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -153,7 +153,7 @@ static void next_trb(struct xhci_hcd *xhci, * See Cycle bit rules. SW is the consumer for the event ring only. * Don't make a ring full of link TRBs. That would be dumb and this would loop. */ -static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) +void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) { /* event ring doesn't have link trbs, check for last trb */ if (ring->type == TYPE_EVENT) { @@ -1141,7 +1141,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, if (xhci->quirks & XHCI_RESET_EP_QUIRK) { struct xhci_command *command; - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) return; @@ -1821,7 +1821,7 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, { struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; struct xhci_command *command; - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) return; @@ -1878,12 +1878,10 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code) static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_ring *ep_ring, int *status) { - struct urb_priv *urb_priv; struct urb *urb = NULL; /* Clean up the endpoint's TD list */ urb = td->urb; - urb_priv = urb->hcpriv; /* if a bounce buffer was used to align this td then unmap it */ xhci_unmap_td_bounce_buffer(xhci, ep_ring, td); @@ -1994,7 +1992,6 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_virt_ep *ep, int *status) { struct xhci_virt_device *xdev; - struct xhci_ring *ep_ring; unsigned int slot_id; int ep_index; struct xhci_ep_ctx *ep_ctx; @@ -2006,7 +2003,6 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); xdev = xhci->devs[slot_id]; ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; - ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); requested = td->urb->transfer_buffer_length; @@ -2477,12 +2473,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, */ if (list_empty(&ep_ring->td_list)) { /* - * A stopped endpoint may generate an extra completion - * event if the device was suspended. Don't print - * warnings. + * Don't print wanings if it's due to a stopped endpoint + * generating an extra completion event if the device + * was suspended. Or, a event for the last TRB of a + * short TD we already got a short event for. + * The short TD is already removed from the TD list. */ + if (!(trb_comp_code == COMP_STOPPED || - trb_comp_code == COMP_STOPPED_LENGTH_INVALID)) { + trb_comp_code == COMP_STOPPED_LENGTH_INVALID || + ep_ring->last_td_was_short)) { xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), ep_index); @@ -2961,7 +2961,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, return 0; } -static unsigned int count_trbs(u64 addr, u64 len) +unsigned int count_trbs(u64 addr, u64 len) { unsigned int num_trbs; @@ -3108,7 +3108,7 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred, { u32 maxp, total_packet_count; - /* MTK xHCI is mostly 0.97 but contains some features from 1.0 */ + /* MTK xHCI 0.96 contains some features from 1.0 */ if (xhci->hci_version < 0x100 && !(xhci->quirks & XHCI_MTK_HOST)) return ((td_total_len - transferred) >> 10); @@ -3117,8 +3117,8 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred, trb_buff_len == td_total_len) return 0; - /* for MTK xHCI, TD size doesn't include this TRB */ - if (xhci->quirks & XHCI_MTK_HOST) + /* for MTK xHCI 0.96, TD size include this TRB, but not in 1.x */ + if ((xhci->quirks & XHCI_MTK_HOST) && (xhci->hci_version < 0x100)) trb_buff_len = 0; maxp = usb_endpoint_maxp(&urb->ep->desc); @@ -4040,7 +4040,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, } /* This function gets called from contexts where it cannot sleep */ - cmd = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!cmd) return; diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index a3b57c781db1..410544ffe78f 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -23,6 +23,7 @@ #include <linux/tracepoint.h> #include "xhci.h" +#include "xhci-dbgcap.h" #define XHCI_MSG_MAX 500 @@ -155,6 +156,21 @@ DEFINE_EVENT(xhci_log_trb, xhci_queue_trb, TP_ARGS(ring, trb) ); +DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_event, + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), + TP_ARGS(ring, trb) +); + +DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_transfer, + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), + TP_ARGS(ring, trb) +); + +DEFINE_EVENT(xhci_log_trb, xhci_dbc_gadget_ep_queue, + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), + TP_ARGS(ring, trb) +); + DECLARE_EVENT_CLASS(xhci_log_virt_dev, TP_PROTO(struct xhci_virt_device *vdev), TP_ARGS(vdev), @@ -478,6 +494,59 @@ DEFINE_EVENT(xhci_log_portsc, xhci_handle_port_status, TP_ARGS(portnum, portsc) ); +DEFINE_EVENT(xhci_log_portsc, xhci_get_port_status, + TP_PROTO(u32 portnum, u32 portsc), + TP_ARGS(portnum, portsc) +); + +DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data, + TP_PROTO(u32 portnum, u32 portsc), + TP_ARGS(portnum, portsc) +); + +DECLARE_EVENT_CLASS(xhci_dbc_log_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req), + TP_STRUCT__entry( + __field(struct dbc_request *, req) + __field(bool, dir) + __field(unsigned int, actual) + __field(unsigned int, length) + __field(int, status) + ), + TP_fast_assign( + __entry->req = req; + __entry->dir = req->direction; + __entry->actual = req->actual; + __entry->length = req->length; + __entry->status = req->status; + ), + TP_printk("%s: req %p length %u/%u ==> %d", + __entry->dir ? "bulk-in" : "bulk-out", + __entry->req, __entry->actual, + __entry->length, __entry->status + ) +); + +DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_alloc_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_free_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_queue_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_giveback_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req) +); #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 2424d3020ca3..1eeb3396300f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -21,6 +21,7 @@ #include "xhci-trace.h" #include "xhci-mtk.h" #include "xhci-debugfs.h" +#include "xhci-dbgcap.h" #define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" @@ -573,10 +574,6 @@ int xhci_run(struct usb_hcd *hcd) if (ret) return ret; - xhci_dbg_cmd_ptrs(xhci); - - xhci_dbg(xhci, "ERST memory map follows:\n"); - xhci_dbg_erst(xhci, &xhci->erst); temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); temp_64 &= ~ERST_PTR_MASK; xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -586,11 +583,7 @@ int xhci_run(struct usb_hcd *hcd) "// Set the interrupt modulation register"); temp = readl(&xhci->ir_set->irq_control); temp &= ~ER_IRQ_INTERVAL_MASK; - /* - * the increment interval is 8 times as much as that defined - * in xHCI spec on MTK's controller - */ - temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160); + temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK; writel(temp, &xhci->ir_set->irq_control); /* Set the HCD state before we enable the irqs */ @@ -605,12 +598,11 @@ int xhci_run(struct usb_hcd *hcd) "// Enabling event ring interrupter %p by writing 0x%x to irq_pending", xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp)); writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); - xhci_print_ir_set(xhci, 0); if (xhci->quirks & XHCI_NEC_HOST) { struct xhci_command *command; - command = xhci_alloc_command(xhci, false, false, GFP_KERNEL); + command = xhci_alloc_command(xhci, false, GFP_KERNEL); if (!command) return -ENOMEM; @@ -622,6 +614,8 @@ int xhci_run(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_run for USB2 roothub"); + xhci_dbc_init(xhci); + xhci_debugfs_init(xhci); return 0; @@ -654,6 +648,8 @@ static void xhci_stop(struct usb_hcd *hcd) xhci_debugfs_exit(xhci); + xhci_dbc_exit(xhci); + spin_lock_irq(&xhci->lock); xhci->xhc_state |= XHCI_STATE_HALTED; xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; @@ -681,7 +677,6 @@ static void xhci_stop(struct usb_hcd *hcd) writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); temp = readl(&xhci->ir_set->irq_pending); writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); - xhci_print_ir_set(xhci, 0); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -870,6 +865,8 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) xhci->shared_hcd->state != HC_STATE_SUSPENDED) return -EINVAL; + xhci_dbc_suspend(xhci); + /* Clear root port wake on bits if wakeup not allowed. */ if (!do_wakeup) xhci_disable_port_wake_on_bits(xhci); @@ -1014,7 +1011,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); temp = readl(&xhci->ir_set->irq_pending); writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); - xhci_print_ir_set(xhci, 0); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -1065,6 +1061,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) spin_unlock_irq(&xhci->lock); + xhci_dbc_resume(xhci); + done: if (retval == 0) { /* Resume root hubs only when have pending events. */ @@ -1243,7 +1241,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, * changes max packet sizes. */ - command = xhci_alloc_command(xhci, false, true, GFP_KERNEL); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) return -ENOMEM; @@ -1498,7 +1496,7 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) * the first cancellation to be handled. */ if (!(ep->ep_state & EP_STOP_CMD_PENDING)) { - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) { ret = -ENOMEM; goto done; @@ -2683,7 +2681,7 @@ static int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev); virt_dev = xhci->devs[udev->slot_id]; - command = xhci_alloc_command(xhci, false, true, GFP_KERNEL); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) return -ENOMEM; @@ -2838,12 +2836,10 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index, unsigned int stream_id, struct xhci_td *td) { struct xhci_dequeue_state deq_state; - struct xhci_virt_ep *ep; struct usb_device *udev = td->urb->dev; 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. */ @@ -3092,7 +3088,7 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, return -ENOSYS; } - config_cmd = xhci_alloc_command(xhci, true, true, mem_flags); + config_cmd = xhci_alloc_command_with_ctx(xhci, true, mem_flags); if (!config_cmd) return -ENOMEM; @@ -3363,7 +3359,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, unsigned int slot_id; struct xhci_virt_device *virt_dev; struct xhci_command *reset_device_cmd; - int last_freed_endpoint; struct xhci_slot_ctx *slot_ctx; int old_active_eps = 0; @@ -3416,7 +3411,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, * reset as part of error handling, so use GFP_NOIO instead of * GFP_KERNEL. */ - reset_device_cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO); + reset_device_cmd = xhci_alloc_command(xhci, true, GFP_NOIO); if (!reset_device_cmd) { xhci_dbg(xhci, "Couldn't allocate command structure.\n"); return -ENOMEM; @@ -3478,7 +3473,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, } /* Everything but endpoint 0 is disabled, so free the rings. */ - last_freed_endpoint = 1; for (i = 1; i < 31; i++) { struct xhci_virt_ep *ep = &virt_dev->eps[i]; @@ -3493,7 +3487,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, if (ep->ring) { xhci_debugfs_remove_endpoint(xhci, virt_dev, i); xhci_free_endpoint_ring(xhci, virt_dev, i); - last_freed_endpoint = i; } if (!list_empty(&virt_dev->eps[i].bw_endpoint_list)) xhci_drop_ep_from_interval_table(xhci, @@ -3525,8 +3518,6 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_slot_ctx *slot_ctx; int i, ret; - xhci_debugfs_remove_slot(xhci, udev->slot_id); - #ifndef CONFIG_USB_DEFAULT_PERSIST /* * We called pm_runtime_get_noresume when the device was attached. @@ -3555,8 +3546,10 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) } ret = xhci_disable_slot(xhci, udev->slot_id); - if (ret) + if (ret) { + xhci_debugfs_remove_slot(xhci, udev->slot_id); xhci_free_virt_device(xhci, udev->slot_id); + } } int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id) @@ -3566,7 +3559,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id) u32 state; int ret = 0; - command = xhci_alloc_command(xhci, false, false, GFP_KERNEL); + command = xhci_alloc_command(xhci, false, GFP_KERNEL); if (!command) return -ENOMEM; @@ -3628,7 +3621,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) int ret, slot_id; struct xhci_command *command; - command = xhci_alloc_command(xhci, false, true, GFP_KERNEL); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) return 0; @@ -3761,7 +3754,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, } } - command = xhci_alloc_command(xhci, false, true, GFP_KERNEL); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) { ret = -ENOMEM; goto out; @@ -4680,7 +4673,7 @@ static int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, return -EINVAL; } - config_cmd = xhci_alloc_command(xhci, true, true, mem_flags); + config_cmd = xhci_alloc_command_with_ctx(xhci, true, mem_flags); if (!config_cmd) return -ENOMEM; @@ -4828,7 +4821,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); - xhci_print_registers(xhci); xhci->quirks |= quirks; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 99a014a920d3..96099a245c69 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1717,6 +1717,8 @@ struct xhci_hcd { u8 max_interrupters; u8 max_ports; u8 isoc_threshold; + /* imod_interval in ns (I * 250ns) */ + u32 imod_interval; int event_ring_max; /* 4KB min, 128MB max */ int page_size; @@ -1856,6 +1858,7 @@ struct xhci_hcd { struct dentry *debugfs_slots; struct list_head regset_list; + void *dbc; /* platform-specific data -- must come last */ unsigned long priv[0] __aligned(sizeof(s64)); }; @@ -1924,12 +1927,6 @@ static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci) } /* xHCI debugging */ -void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num); -void xhci_print_registers(struct xhci_hcd *xhci); -void xhci_dbg_regs(struct xhci_hcd *xhci); -void xhci_print_run_regs(struct xhci_hcd *xhci); -void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst); -void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci); char *xhci_get_slot_state(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), @@ -1965,9 +1962,17 @@ void xhci_slot_copy(struct xhci_hcd *xhci, int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *udev, struct usb_host_endpoint *ep, gfp_t mem_flags); +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, + unsigned int num_segs, unsigned int cycle_state, + enum xhci_ring_type type, unsigned int max_packet, gfp_t flags); void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring); int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, - unsigned int num_trbs, gfp_t flags); + unsigned int num_trbs, gfp_t flags); +int xhci_alloc_erst(struct xhci_hcd *xhci, + struct xhci_ring *evt_ring, + struct xhci_erst *erst, + gfp_t flags); +void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst); void xhci_free_endpoint_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, unsigned int ep_index); @@ -1992,11 +1997,16 @@ struct xhci_ring *xhci_stream_id_to_ring( unsigned int ep_index, unsigned int stream_id); struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, - bool allocate_in_ctx, bool allocate_completion, - gfp_t mem_flags); + bool allocate_completion, gfp_t mem_flags); +struct xhci_command *xhci_alloc_command_with_ctx(struct xhci_hcd *xhci, + bool allocate_completion, gfp_t mem_flags); void xhci_urb_free_priv(struct urb_priv *urb_priv); void xhci_free_command(struct xhci_hcd *xhci, struct xhci_command *command); +struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, + int type, gfp_t flags); +void xhci_free_container_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx); /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); @@ -2070,6 +2080,8 @@ void xhci_handle_command_timeout(struct work_struct *work); void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); void xhci_cleanup_command_queue(struct xhci_hcd *xhci); +void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring); +unsigned int count_trbs(u64 addr, u64 len); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index aedc9a7f149e..90028ef541e3 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1710,6 +1710,35 @@ static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb) return 0; } +static int test_toggle_sync(struct usbtest_dev *tdev, int ep, struct urb *urb) +{ + int retval; + + /* clear initial data toggle to DATA0 */ + retval = usb_clear_halt(urb->dev, urb->pipe); + if (retval < 0) { + ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval); + return retval; + } + + /* transfer 3 data packets, should be DATA0, DATA1, DATA0 */ + retval = simple_io(tdev, urb, 1, 0, 0, __func__); + if (retval != 0) + return -EINVAL; + + /* clear halt resets device side data toggle, host should react to it */ + retval = usb_clear_halt(urb->dev, urb->pipe); + if (retval < 0) { + ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval); + return retval; + } + + /* host should use DATA0 again after clear halt */ + retval = simple_io(tdev, urb, 1, 0, 0, __func__); + + return retval; +} + static int halt_simple(struct usbtest_dev *dev) { int ep; @@ -1742,6 +1771,33 @@ done: return retval; } +static int toggle_sync_simple(struct usbtest_dev *dev) +{ + int ep; + int retval = 0; + struct urb *urb; + struct usb_device *udev = testdev_to_usbdev(dev); + unsigned maxp = get_maxpacket(udev, dev->out_pipe); + + /* + * Create a URB that causes a transfer of uneven amount of data packets + * This way the clear toggle has an impact on the data toggle sequence. + * Use 2 maxpacket length packets and one zero packet. + */ + urb = simple_alloc_urb(udev, 0, 2 * maxp, 0); + if (urb == NULL) + return -ENOMEM; + + urb->transfer_flags |= URB_ZERO_PACKET; + + ep = usb_pipeendpoint(dev->out_pipe); + urb->pipe = dev->out_pipe; + retval = test_toggle_sync(dev, ep, urb); + + simple_free_urb(urb); + return retval; +} + /*-------------------------------------------------------------------------*/ /* Control OUT tests use the vendor control requests from Intel's @@ -2524,6 +2580,20 @@ usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param) retval = test_queue(dev, param, dev->in_pipe, NULL, 0); break; + /* Test data Toggle/seq_nr clear between bulk out transfers */ + case 29: + if (dev->out_pipe == 0) + break; + retval = 0; + dev_info(&intf->dev, "TEST 29: Clear toggle between bulk writes %d times\n", + param->iterations); + for (i = param->iterations; retval == 0 && i > 0; --i) + retval = toggle_sync_simple(dev); + + if (retval) + ERROR(dev, "toggle sync failed, iterations left %d\n", + i); + break; } return retval; } diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 3c888d942a9f..2cd00a24afd9 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -229,7 +229,10 @@ struct otg_switch_mtk { * @u3p_dis_msk: mask of disabling usb3 ports, for example, bit0==1 to * disable u3port0, bit1==1 to disable u3port1,... etc * @dbgfs_root: only used when supports manual dual-role switch via debugfs - * @wakeup_en: it's true when supports remote wakeup in host mode + * @uwk_en: it's true when supports remote wakeup in host mode + * @uwk: syscon including usb wakeup glue layer between SSUSB IP and SPM + * @uwk_reg_base: the base address of the wakeup glue layer in @uwk + * @uwk_vers: the version of the wakeup glue layer */ struct ssusb_mtk { struct device *dev; @@ -253,8 +256,10 @@ struct ssusb_mtk { int u3p_dis_msk; struct dentry *dbgfs_root; /* usb wakeup for host mode */ - bool wakeup_en; - struct regmap *pericfg; + bool uwk_en; + struct regmap *uwk; + u32 uwk_reg_base; + u32 uwk_vers; }; /** diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h index c179192408ba..ae1598d76e02 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -18,8 +18,7 @@ int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb, struct device_node *dn); int ssusb_host_enable(struct ssusb_mtk *ssusb); int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend); -int ssusb_wakeup_enable(struct ssusb_mtk *ssusb); -void ssusb_wakeup_disable(struct ssusb_mtk *ssusb); +void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable); #else diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index d237d7e65c44..259beefe3b3b 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -18,66 +18,77 @@ #include "mtu3.h" #include "mtu3_dr.h" -#define PERI_WK_CTRL1 0x404 -#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26) -#define UWK_CTL1_IS_E BIT(25) -#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */ -#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */ -#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */ -#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */ +/* mt8173 etc */ +#define PERI_WK_CTRL1 0x4 +#define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */ +#define WC1_IS_EN BIT(25) +#define WC1_IS_P BIT(6) /* polarity for ip sleep */ + +/* mt2712 etc */ +#define PERI_SSUSB_SPM_CTRL 0x0 +#define SSC_IP_SLEEP_EN BIT(4) +#define SSC_SPM_INT_EN BIT(1) + +enum ssusb_uwk_vers { + SSUSB_UWK_V1 = 1, + SSUSB_UWK_V2, +}; /* * ip-sleep wakeup mode: * all clocks can be turn off, but power domain should be kept on */ -static void ssusb_wakeup_ip_sleep_en(struct ssusb_mtk *ssusb) +static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable) { - u32 tmp; - struct regmap *pericfg = ssusb->pericfg; - - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_IS_P; - tmp &= ~(UWK_CTL1_IS_C(0xf)); - tmp |= UWK_CTL1_IS_C(0x8); - regmap_write(pericfg, PERI_WK_CTRL1, tmp); - regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E); - - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - dev_dbg(ssusb->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n", - __func__, tmp); -} - -static void ssusb_wakeup_ip_sleep_dis(struct ssusb_mtk *ssusb) -{ - u32 tmp; - - regmap_read(ssusb->pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_IS_E; - regmap_write(ssusb->pericfg, PERI_WK_CTRL1, tmp); + u32 reg, msk, val; + + switch (ssusb->uwk_vers) { + case SSUSB_UWK_V1: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL1; + msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P; + val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0; + break; + case SSUSB_UWK_V2: + reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL; + msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; + val = enable ? msk : 0; + break; + default: + return; + }; + regmap_update_bits(ssusb->uwk, reg, msk, val); } int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb, struct device_node *dn) { - struct device *dev = ssusb->dev; + struct of_phandle_args args; + int ret; - /* - * Wakeup function is optional, so it is not an error if this property - * does not exist, and in such case, no need to get relative - * properties anymore. - */ - ssusb->wakeup_en = of_property_read_bool(dn, "mediatek,enable-wakeup"); - if (!ssusb->wakeup_en) + /* wakeup function is optional */ + ssusb->uwk_en = of_property_read_bool(dn, "wakeup-source"); + if (!ssusb->uwk_en) return 0; - ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn, - "mediatek,syscon-wakeup"); - if (IS_ERR(ssusb->pericfg)) { - dev_err(dev, "fail to get pericfg regs\n"); - return PTR_ERR(ssusb->pericfg); - } + ret = of_parse_phandle_with_fixed_args(dn, + "mediatek,syscon-wakeup", 2, 0, &args); + if (ret) + return ret; - return 0; + ssusb->uwk_reg_base = args.args[0]; + ssusb->uwk_vers = args.args[1]; + ssusb->uwk = syscon_node_to_regmap(args.np); + of_node_put(args.np); + dev_info(ssusb->dev, "uwk - reg:0x%x, version:%d\n", + ssusb->uwk_reg_base, ssusb->uwk_vers); + + return PTR_ERR_OR_ZERO(ssusb->uwk); +} + +void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable) +{ + if (ssusb->uwk_en) + ssusb_wakeup_ip_sleep_set(ssusb, enable); } static void host_ports_num_get(struct ssusb_mtk *ssusb) @@ -235,17 +246,3 @@ void ssusb_host_exit(struct ssusb_mtk *ssusb) of_platform_depopulate(ssusb->dev); ssusb_host_cleanup(ssusb); } - -int ssusb_wakeup_enable(struct ssusb_mtk *ssusb) -{ - if (ssusb->wakeup_en) - ssusb_wakeup_ip_sleep_en(ssusb); - - return 0; -} - -void ssusb_wakeup_disable(struct ssusb_mtk *ssusb) -{ - if (ssusb->wakeup_en) - ssusb_wakeup_ip_sleep_dis(ssusb); -} diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 3650fd11fc49..628d5ce356ca 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -282,8 +282,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) /* if host role is supported */ ret = ssusb_wakeup_of_property_parse(ssusb, node); - if (ret) + if (ret) { + dev_err(dev, "failed to parse uwk property\n"); return ret; + } /* optional property, ignore the error if it does not exist */ of_property_read_u32(node, "mediatek,u3p-dis-msk", @@ -308,7 +310,7 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0); if (IS_ERR(otg_sx->edev)) { dev_err(ssusb->dev, "couldn't get extcon device\n"); - return -EPROBE_DEFER; + return PTR_ERR(otg_sx->edev); } } @@ -457,7 +459,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev) ssusb_host_disable(ssusb, true); ssusb_phy_power_off(ssusb); ssusb_clks_disable(ssusb); - ssusb_wakeup_enable(ssusb); + ssusb_wakeup_set(ssusb, true); return 0; } @@ -473,7 +475,7 @@ static int __maybe_unused mtu3_resume(struct device *dev) if (!ssusb->is_host) return 0; - ssusb_wakeup_disable(ssusb); + ssusb_wakeup_set(ssusb, false); ret = ssusb_clks_enable(ssusb); if (ret) goto clks_err; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 0397606a211b..b8295ce7c4fe 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -284,7 +284,15 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; portstate(musb->port1_status |= USB_PORT_STAT_POWER); del_timer(&musb->dev_timer); - } else { + } else if (!(musb->int_usb & MUSB_INTR_BABBLE)) { + /* + * When babble condition happens, drvvbus interrupt + * is also generated. Ignore this drvvbus interrupt + * and let babble interrupt handler recovers the + * controller; otherwise, the host-mode flag is lost + * due to the MUSB_DEV_MODE() call below and babble + * recovery logic will not be called. + */ musb->is_active = 0; MUSB_DEV_MODE(musb); otg->default_a = 0; @@ -512,7 +520,7 @@ static int da8xx_probe(struct platform_device *pdev) if (!glue) return -ENOMEM; - clk = devm_clk_get(&pdev->dev, "usb20"); + clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); return PTR_ERR(clk); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 2627363fb4fe..394b4ac86161 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -195,7 +195,6 @@ static struct musb_qh *musb_ep_get_qh(struct musb_hw_ep *ep, int is_in) static void musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) { - u16 frame; u32 len; void __iomem *mbase = musb->mregs; struct urb *urb = next_urb(qh); @@ -244,7 +243,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: musb_dbg(musb, "check whether there's still time for periodic Tx"); - frame = musb_readw(mbase, MUSB_FRAME); /* FIXME this doesn't implement that scheduling policy ... * or handle framecounter wrapping */ @@ -1781,7 +1779,6 @@ void musb_host_rx(struct musb *musb, u8 epnum) struct musb_qh *qh = hw_ep->in_qh; size_t xfer_len; void __iomem *mbase = musb->mregs; - int pipe; u16 rx_csr, val; bool iso_err = false; bool done = false; @@ -1810,8 +1807,6 @@ void musb_host_rx(struct musb *musb, u8 epnum) return; } - pipe = urb->pipe; - trace_musb_urb_rx(musb, urb); /* check for errors, concurrent stall & unlink is not really diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index f97cb47577fc..bceb2c9988dd 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -323,6 +323,14 @@ static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) return *phy == match_data; } +static void usb_charger_init(struct usb_phy *usb_phy) +{ + usb_phy->chg_type = UNKNOWN_TYPE; + usb_phy->chg_state = USB_CHARGER_DEFAULT; + usb_phy_set_default_current(usb_phy); + INIT_WORK(&usb_phy->chg_work, usb_phy_notify_charger_work); +} + static int usb_add_extcon(struct usb_phy *x) { int ret; @@ -406,10 +414,6 @@ static int usb_add_extcon(struct usb_phy *x) } } - usb_phy_set_default_current(x); - INIT_WORK(&x->chg_work, usb_phy_notify_charger_work); - x->chg_type = UNKNOWN_TYPE; - x->chg_state = USB_CHARGER_DEFAULT; if (x->type_nb.notifier_call) __usb_phy_get_charger_type(x); @@ -704,6 +708,7 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) return -EINVAL; } + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) return ret; @@ -749,6 +754,7 @@ int usb_add_phy_dev(struct usb_phy *x) return -EINVAL; } + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) return ret; diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index fac147a3ad23..5c5b51bb48ef 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o -renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o rcar3.o +renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o rcar3.o rza.o ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) renesas_usbhs-y += mod_host.o diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 56079bb6759a..4310df46639d 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -17,6 +17,7 @@ #include "common.h" #include "rcar2.h" #include "rcar3.h" +#include "rza.h" /* * image of renesas_usbhs @@ -488,6 +489,10 @@ static const struct of_device_id usbhs_of_match[] = { .compatible = "renesas,rcar-gen3-usbhs", .data = (void *)USBHS_TYPE_RCAR_GEN3, }, + { + .compatible = "renesas,rza1-usbhs", + .data = (void *)USBHS_TYPE_RZA1, + }, { }, }; MODULE_DEVICE_TABLE(of, usbhs_of_match); @@ -520,6 +525,11 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe); } + if (dparam->type == USBHS_TYPE_RZA1) { + dparam->pipe_configs = usbhsc_new_pipe; + dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe); + } + return info; } @@ -581,6 +591,18 @@ static int usbhs_probe(struct platform_device *pdev) break; case USBHS_TYPE_RCAR_GEN3_WITH_PLL: priv->pfunc = usbhs_rcar3_with_pll_ops; + if (!IS_ERR_OR_NULL(priv->edev)) { + priv->nb.notifier_call = priv->pfunc.notifier; + ret = devm_extcon_register_notifier(&pdev->dev, + priv->edev, + EXTCON_USB_HOST, + &priv->nb); + if (ret < 0) + dev_err(&pdev->dev, "no notifier registered\n"); + } + break; + case USBHS_TYPE_RZA1: + priv->pfunc = usbhs_rza1_ops; break; default: if (!info->platform_callback.get_id) { diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 64797784a6df..f619afeae2b8 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -98,6 +98,7 @@ struct usbhs_priv; #define D2FIFOCTR 0x00F2 /* for R-Car Gen2 */ #define D3FIFOSEL 0x00F4 /* for R-Car Gen2 */ #define D3FIFOCTR 0x00F6 /* for R-Car Gen2 */ +#define SUSPMODE 0x0102 /* for RZ/A */ /* SYSCFG */ #define SCKE (1 << 10) /* USB Module Clock Enable */ @@ -106,6 +107,8 @@ struct usbhs_priv; #define DRPD (1 << 5) /* D+ Line/D- Line Resistance Control */ #define DPRPU (1 << 4) /* D+ Line Resistance Control */ #define USBE (1 << 0) /* USB Module Operation Enable */ +#define UCKSEL (1 << 2) /* Clock Select for RZ/A1 */ +#define UPLLE (1 << 1) /* USB PLL Enable for RZ/A1 */ /* DVSTCTR */ #define EXTLP (1 << 10) /* Controls the EXTLP pin output state */ @@ -233,6 +236,9 @@ struct usbhs_priv; #define USBSPD_SPEED_FULL 0x2 #define USBSPD_SPEED_HIGH 0x3 +/* SUSPMODE */ +#define SUSPM (1 << 14) /* SuspendM Control */ + /* * struct */ @@ -249,6 +255,7 @@ struct usbhs_priv { struct platform_device *pdev; struct extcon_dev *edev; + struct notifier_block nb; spinlock_t lock; diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 2d24ef3076ef..5925d111bd47 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -94,8 +94,6 @@ static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe) return list_first_entry_or_null(&pipe->list, struct usbhs_pkt, node); } -static void usbhsf_fifo_clear(struct usbhs_pipe *pipe, - struct usbhs_fifo *fifo); static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo, @@ -124,10 +122,11 @@ struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt) chan = usbhsf_dma_chan_get(fifo, pkt); if (chan) { dmaengine_terminate_all(chan); - usbhsf_fifo_clear(pipe, fifo); usbhsf_dma_unmap(pkt); } + usbhs_pipe_clear_without_sequence(pipe, 0, 0); + __usbhsf_pkt_del(pkt); } @@ -256,15 +255,9 @@ static void usbhsf_send_terminator(struct usbhs_pipe *pipe, static int usbhsf_fifo_barrier(struct usbhs_priv *priv, struct usbhs_fifo *fifo) { - int timeout = 1024; - - do { - /* The FIFO port is accessible */ - if (usbhs_read(priv, fifo->ctr) & FRDY) - return 0; - - udelay(10); - } while (timeout--); + /* The FIFO port is accessible */ + if (usbhs_read(priv, fifo->ctr) & FRDY) + return 0; return -EBUSY; } @@ -278,8 +271,8 @@ static void usbhsf_fifo_clear(struct usbhs_pipe *pipe, if (!usbhs_pipe_is_dcp(pipe)) { /* * This driver checks the pipe condition first to avoid -EBUSY - * from usbhsf_fifo_barrier() with about 10 msec delay in - * the interrupt handler if the pipe is RX direction and empty. + * from usbhsf_fifo_barrier() if the pipe is RX direction and + * empty. */ if (usbhs_pipe_is_dir_in(pipe)) ret = usbhs_pipe_is_accessible(pipe); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 093cd8e87335..9677e0e31475 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -590,10 +590,22 @@ void usbhs_pipe_clear(struct usbhs_pipe *pipe) } } -void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable) +/* Should call usbhsp_pipe_select() before */ +void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe, + int needs_bfre, int bfre_enable) { int sequence; + usbhsp_pipe_select(pipe); + sequence = usbhs_pipe_get_data_sequence(pipe); + if (needs_bfre) + usbhsp_pipe_cfg_set(pipe, BFRE, bfre_enable ? BFRE : 0); + usbhs_pipe_clear(pipe); + usbhs_pipe_data_sequence(pipe, sequence); +} + +void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable) +{ if (usbhs_pipe_is_dcp(pipe)) return; @@ -602,10 +614,7 @@ void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable) if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE))) return; - sequence = usbhs_pipe_get_data_sequence(pipe); - usbhsp_pipe_cfg_set(pipe, BFRE, enable ? BFRE : 0); - usbhs_pipe_clear(pipe); - usbhs_pipe_data_sequence(pipe, sequence); + usbhs_pipe_clear_without_sequence(pipe, 1, enable); } static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index d3d002244891..3080423e600c 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -80,6 +80,8 @@ void usbhs_pipe_init(struct usbhs_priv *priv, struct usbhs_pkt *pkt, int map)); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); void usbhs_pipe_clear(struct usbhs_pipe *pipe); +void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe, + int needs_bfre, int bfre_enable); int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe); void usbhs_pipe_enable(struct usbhs_pipe *pipe); void usbhs_pipe_disable(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c index c929d296c77b..d0ea4ff89622 100644 --- a/drivers/usb/renesas_usbhs/rcar3.c +++ b/drivers/usb/renesas_usbhs/rcar3.c @@ -27,6 +27,7 @@ * Remarks: bit[31:11] and bit[9:6] should be 0 */ #define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */ +#define UGCTRL2_USB0SEL_EHCI 0x00000010 #define UGCTRL2_USB0SEL_HSUSB 0x00000020 #define UGCTRL2_USB0SEL_OTG 0x00000030 #define UGCTRL2_VBUSSEL 0x00000400 @@ -44,13 +45,25 @@ static u32 usbhs_read32(struct usbhs_priv *priv, u32 reg) return ioread32(priv->base + reg); } +static void usbhs_rcar3_set_ugctrl2(struct usbhs_priv *priv, u32 val) +{ + usbhs_write32(priv, UGCTRL2, val | UGCTRL2_RESERVED_3); +} + +static void usbhs_rcar3_set_usbsel(struct usbhs_priv *priv, bool ehci) +{ + if (ehci) + usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_EHCI); + else + usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_HSUSB); +} + static int usbhs_rcar3_power_ctrl(struct platform_device *pdev, void __iomem *base, int enable) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); - usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | UGCTRL2_USB0SEL_OTG | - UGCTRL2_VBUSSEL); + usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_OTG | UGCTRL2_VBUSSEL); if (enable) { usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM); @@ -70,11 +83,14 @@ static int usbhs_rcar3_power_and_pll_ctrl(struct platform_device *pdev, struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); u32 val; int timeout = 1000; + bool is_host = false; if (enable) { usbhs_write32(priv, UGCTRL, 0); /* release PLLRESET */ - usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | - UGCTRL2_USB0SEL_HSUSB); + if (priv->edev) + is_host = extcon_get_state(priv->edev, EXTCON_USB_HOST); + + usbhs_rcar3_set_usbsel(priv, is_host); usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM); do { @@ -96,6 +112,16 @@ static int usbhs_rcar3_get_id(struct platform_device *pdev) return USBHS_GADGET; } +static int usbhs_rcar3_notifier(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct usbhs_priv *priv = container_of(nb, struct usbhs_priv, nb); + + usbhs_rcar3_set_usbsel(priv, !!event); + + return NOTIFY_DONE; +} + const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = { .power_ctrl = usbhs_rcar3_power_ctrl, .get_id = usbhs_rcar3_get_id, @@ -104,4 +130,5 @@ const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = { const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = { .power_ctrl = usbhs_rcar3_power_and_pll_ctrl, .get_id = usbhs_rcar3_get_id, + .notifier = usbhs_rcar3_notifier, }; diff --git a/drivers/usb/renesas_usbhs/rza.c b/drivers/usb/renesas_usbhs/rza.c new file mode 100644 index 000000000000..5b287257ec11 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rza.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas USB driver RZ/A initialization and power control + * + * Copyright (C) 2018 Chris Brandt + * Copyright (C) 2018 Renesas Electronics Corporation + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of_device.h> +#include "common.h" +#include "rza.h" + +static int usbhs_rza1_hardware_init(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + struct device_node *usb_x1_clk, *extal_clk; + u32 freq_usb = 0, freq_extal = 0; + + /* Input Clock Selection (NOTE: ch0 controls both ch0 and ch1) */ + usb_x1_clk = of_find_node_by_name(NULL, "usb_x1"); + extal_clk = of_find_node_by_name(NULL, "extal"); + of_property_read_u32(usb_x1_clk, "clock-frequency", &freq_usb); + of_property_read_u32(extal_clk, "clock-frequency", &freq_extal); + if (freq_usb == 0) { + if (freq_extal == 12000000) { + /* Select 12MHz XTAL */ + usbhs_bset(priv, SYSCFG, UCKSEL, UCKSEL); + } else { + dev_err(usbhs_priv_to_dev(priv), "A 48MHz USB clock or 12MHz main clock is required.\n"); + return -EIO; + } + } + + /* Enable USB PLL (NOTE: ch0 controls both ch0 and ch1) */ + usbhs_bset(priv, SYSCFG, UPLLE, UPLLE); + udelay(1000); + usbhs_bset(priv, SUSPMODE, SUSPM, SUSPM); + + return 0; +} + +static int usbhs_rza_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +const struct renesas_usbhs_platform_callback usbhs_rza1_ops = { + .hardware_init = usbhs_rza1_hardware_init, + .get_id = usbhs_rza_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rza.h b/drivers/usb/renesas_usbhs/rza.h new file mode 100644 index 000000000000..ca917ca54f6d --- /dev/null +++ b/drivers/usb/renesas_usbhs/rza.h @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "common.h" + +extern const struct renesas_usbhs_platform_callback usbhs_rza1_ops; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 1aba9105b369..fc68952c994a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1013,6 +1013,7 @@ static const struct usb_device_id id_table_combined[] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) }, { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) }, + { USB_DEVICE(AIRBUS_DS_VID, AIRBUS_DS_P8GR) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 4faa09fe308c..8b4ecd2bd297 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -915,6 +915,12 @@ #define ICPDAS_I7563U_PID 0x0105 /* + * Airbus Defence and Space + */ +#define AIRBUS_DS_VID 0x1e8e /* Vendor ID */ +#define AIRBUS_DS_P8GR 0x6001 /* Tetra P8GR */ + +/* * RT Systems programming cables for various ham radios */ #define RTSYSTEMS_VID 0x2100 /* Vendor ID */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index aaa7d901a06d..b6320e3be429 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -233,11 +233,14 @@ static void option_instat_callback(struct urb *urb); /* These Quectel products use Qualcomm's vendor ID */ #define QUECTEL_PRODUCT_UC20 0x9003 #define QUECTEL_PRODUCT_UC15 0x9090 +/* These Yuga products use Qualcomm's vendor ID */ +#define YUGA_PRODUCT_CLM920_NC5 0x9625 #define QUECTEL_VENDOR_ID 0x2c7c /* These Quectel products use Quectel's vendor ID */ #define QUECTEL_PRODUCT_EC21 0x0121 #define QUECTEL_PRODUCT_EC25 0x0125 +#define QUECTEL_PRODUCT_BG96 0x0296 #define CMOTECH_VENDOR_ID 0x16d8 #define CMOTECH_PRODUCT_6001 0x6001 @@ -279,6 +282,7 @@ static void option_instat_callback(struct urb *urb); #define TELIT_PRODUCT_LE922_USBCFG3 0x1043 #define TELIT_PRODUCT_LE922_USBCFG5 0x1045 #define TELIT_PRODUCT_ME910 0x1100 +#define TELIT_PRODUCT_ME910_DUAL_MODEM 0x1101 #define TELIT_PRODUCT_LE920 0x1200 #define TELIT_PRODUCT_LE910 0x1201 #define TELIT_PRODUCT_LE910_USBCFG4 0x1206 @@ -644,6 +648,11 @@ static const struct option_blacklist_info telit_me910_blacklist = { .reserved = BIT(1) | BIT(3), }; +static const struct option_blacklist_info telit_me910_dual_modem_blacklist = { + .sendsetup = BIT(0), + .reserved = BIT(3), +}; + static const struct option_blacklist_info telit_le910_blacklist = { .sendsetup = BIT(0), .reserved = BIT(1) | BIT(2), @@ -673,6 +682,10 @@ static const struct option_blacklist_info cinterion_rmnet2_blacklist = { .reserved = BIT(4) | BIT(5), }; +static const struct option_blacklist_info yuga_clm920_nc5_blacklist = { + .reserved = BIT(1) | BIT(4), +}; + static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -1177,11 +1190,16 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)}, { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + /* Yuga products use Qualcomm vendor ID */ + { USB_DEVICE(QUALCOMM_VENDOR_ID, YUGA_PRODUCT_CLM920_NC5), + .driver_info = (kernel_ulong_t)&yuga_clm920_nc5_blacklist }, /* Quectel products using Quectel vendor ID */ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003), @@ -1241,6 +1259,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), .driver_info = (kernel_ulong_t)&telit_me910_blacklist }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM), + .driver_info = (kernel_ulong_t)&telit_me910_dual_modem_blacklist }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910), .driver_info = (kernel_ulong_t)&telit_le910_blacklist }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4), diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index e3892541a489..613f91add03d 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -162,6 +162,8 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x1199, 0x9079)}, /* Sierra Wireless EM74xx */ {DEVICE_SWI(0x1199, 0x907a)}, /* Sierra Wireless EM74xx QDL */ {DEVICE_SWI(0x1199, 0x907b)}, /* Sierra Wireless EM74xx */ + {DEVICE_SWI(0x1199, 0x9090)}, /* Sierra Wireless EM7565 QDL */ + {DEVICE_SWI(0x1199, 0x9091)}, /* Sierra Wireless EM7565 */ {DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ @@ -342,6 +344,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) break; case 2: dev_dbg(dev, "NMEA GPS interface found\n"); + sendsetup = true; break; case 3: dev_dbg(dev, "Modem port found\n"); diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index ab5a2ac4993a..aaf4813e4971 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -31,12 +31,14 @@ static const struct usb_device_id id_table[] = { }; static const struct usb_device_id dbc_id_table[] = { + { USB_DEVICE(0x1d6b, 0x0010) }, { USB_DEVICE(0x1d6b, 0x0011) }, { }, }; static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(0x0525, 0x127a) }, + { USB_DEVICE(0x1d6b, 0x0010) }, { USB_DEVICE(0x1d6b, 0x0011) }, { }, }; diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index 1fcd758a961f..3734a25e09e5 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -112,6 +112,10 @@ static int uas_use_uas_driver(struct usb_interface *intf, } } + /* All Seagate disk enclosures have broken ATA pass-through support */ + if (le16_to_cpu(udev->descriptor.idVendor) == 0x0bc2) + flags |= US_FL_NO_ATA_1X; + usb_stor_adjust_quirks(udev, &flags); if (flags & US_FL_IGNORE_UAS) { diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 2968046e7c05..f72d045ee9ef 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2100,6 +2100,13 @@ UNUSUAL_DEV( 0x152d, 0x0567, 0x0114, 0x0116, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_BROKEN_FUA ), +/* Reported by David Kozub <zub@linux.fjfi.cvut.cz> */ +UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999, + "JMicron", + "JMS567", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA), + /* * Reported by Alexandre Oliva <oliva@lsd.ic.unicamp.br> * JMicron responds to USN and several other SCSI ioctls with a diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index d520374a824e..9c2ee55ad32e 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -38,20 +38,6 @@ UNUSUAL_DEV(0x0984, 0x0301, 0x0128, 0x0128, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_IGNORE_UAS), -/* https://bugzilla.kernel.org/show_bug.cgi?id=79511 */ -UNUSUAL_DEV(0x0bc2, 0x2312, 0x0000, 0x9999, - "Seagate", - "Expansion Desk", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* https://bbs.archlinux.org/viewtopic.php?id=183190 */ -UNUSUAL_DEV(0x0bc2, 0x3312, 0x0000, 0x9999, - "Seagate", - "Expansion Desk", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - /* Reported-by: David Webb <djw@noc.ac.uk> */ UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999, "Seagate", @@ -59,55 +45,6 @@ UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_LUNS), -/* Reported-by: Hans de Goede <hdegoede@redhat.com> */ -UNUSUAL_DEV(0x0bc2, 0x3320, 0x0000, 0x9999, - "Seagate", - "Expansion Desk", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* Reported-by: Bogdan Mihalcea <bogdan.mihalcea@infim.ro> */ -UNUSUAL_DEV(0x0bc2, 0xa003, 0x0000, 0x9999, - "Seagate", - "Backup Plus", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* Reported-by: Marcin ZajÄ…czkowski <mszpak@wp.pl> */ -UNUSUAL_DEV(0x0bc2, 0xa013, 0x0000, 0x9999, - "Seagate", - "Backup Plus", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* Reported-by: Hans de Goede <hdegoede@redhat.com> */ -UNUSUAL_DEV(0x0bc2, 0xa0a4, 0x0000, 0x9999, - "Seagate", - "Backup Plus Desk", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* https://bbs.archlinux.org/viewtopic.php?id=183190 */ -UNUSUAL_DEV(0x0bc2, 0xab20, 0x0000, 0x9999, - "Seagate", - "Backup+ BK", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* https://bbs.archlinux.org/viewtopic.php?id=183190 */ -UNUSUAL_DEV(0x0bc2, 0xab21, 0x0000, 0x9999, - "Seagate", - "Backup+ BK", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* Reported-by: G. Richard Bellamy <rbellamy@pteradigm.com> */ -UNUSUAL_DEV(0x0bc2, 0xab2a, 0x0000, 0x9999, - "Seagate", - "BUP Fast HDD", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - /* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */ UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999, "Initio Corporation", @@ -129,6 +66,13 @@ UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_BROKEN_FUA | US_FL_NO_REPORT_OPCODES), +/* Reported-by: David Kozub <zub@linux.fjfi.cvut.cz> */ +UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999, + "JMicron", + "JMS567", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA), + /* Reported-by: Hans de Goede <hdegoede@redhat.com> */ UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999, "VIA", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index a0c07e05a8f1..3eb934792465 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -211,8 +211,8 @@ int usb_stor_reset_resume(struct usb_interface *iface) usb_stor_report_bus_reset(us); /* - * FIXME: Notify the subdrivers that they need to reinitialize - * the device + * If any of the subdrivers implemented a reinitialization scheme, + * this is where the callback would be invoked. */ return 0; } @@ -243,8 +243,8 @@ int usb_stor_post_reset(struct usb_interface *iface) usb_stor_report_bus_reset(us); /* - * FIXME: Notify the subdrivers that they need to reinitialize - * the device + * If any of the subdrivers implemented a reinitialization scheme, + * this is where the callback would be invoked. */ mutex_unlock(&us->dev_mutex); diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 465d7da849c3..bcb2744c5977 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -1,13 +1,53 @@ -menu "USB Power Delivery and Type-C drivers" +menuconfig TYPEC + tristate "USB Type-C Support" + help + USB Type-C Specification defines a cable and connector for USB where + only one type of plug is supported on both ends, i.e. there will not + be Type-A plug on one end of the cable and Type-B plug on the other. + Determination of the host-to-device relationship happens through a + specific Configuration Channel (CC) which goes through the USB Type-C + cable. The Configuration Channel may also be used to detect optional + Accessory Modes - Analog Audio and Debug - and if USB Power Delivery + is supported, the Alternate Modes, where the connector is used for + something else then USB communication. + + USB Power Delivery Specification defines a protocol that can be used + to negotiate the voltage and current levels with the connected + partners. USB Power Delivery allows higher voltages then the normal + 5V, up to 20V, and current up to 5A over the cable. The USB Power + Delivery protocol is also used to negotiate the optional Alternate + Modes when they are supported. USB Power Delivery does not depend on + USB Type-C connector, however it is mostly used together with USB + Type-C connectors. + + USB Type-C and USB Power Delivery Specifications define a set of state + machines that need to be implemented in either software or firmware. + Simple USB Type-C PHYs, for example USB Type-C Port Controller + Interface Specification compliant "Port Controllers" need the state + machines to be handled in the OS, but stand-alone USB Type-C and Power + Delivery controllers handle the state machines inside their firmware. + The USB Type-C and Power Delivery controllers usually function + autonomously, and do not necessarily require drivers. + + Enable this configurations option if you have USB Type-C connectors on + your system and 1) you know your USB Type-C hardware requires OS + control (a driver) to function, or 2) if you need to be able to read + the status of the USB Type-C ports in your system, or 3) if you need + to be able to swap the power role (decide are you supplying or + consuming power over the cable) or data role (host or device) when + both roles are supported. + + For more information, see the kernel documentation for USB Type-C + Connector Class API (Documentation/driver-api/usb/typec.rst) + <https://www.kernel.org/doc/html/latest/driver-api/usb/typec.html> + and ABI (Documentation/ABI/testing/sysfs-class-typec). -config TYPEC - tristate +if TYPEC config TYPEC_TCPM tristate "USB Type-C Port Controller Manager" depends on USB - select TYPEC help The Type-C Port Controller Manager provides a USB PD and USB Type-C state machine for use with Type-C Port Controllers. @@ -22,7 +62,6 @@ config TYPEC_WCOVE depends on INTEL_SOC_PMIC depends on INTEL_PMC_IPC depends on BXT_WC_PMIC_OPREGION - select TYPEC help This driver adds support for USB Type-C detection on Intel Broxton platforms that have Intel Whiskey Cove PMIC. The driver can detect the @@ -31,14 +70,13 @@ config TYPEC_WCOVE To compile this driver as module, choose M here: the module will be called typec_wcove -endif +endif # TYPEC_TCPM source "drivers/usb/typec/ucsi/Kconfig" config TYPEC_TPS6598X tristate "TI TPS6598x USB Power Delivery controller driver" depends on I2C - select TYPEC help Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power Delivery controller. @@ -46,4 +84,4 @@ config TYPEC_TPS6598X If you choose to build this driver as a dynamically linked module, the module will be called tps6598x.ko. -endmenu +endif # TYPEC diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c index 72cb060b3fca..9ce4756adad6 100644 --- a/drivers/usb/typec/fusb302/fusb302.c +++ b/drivers/usb/typec/fusb302/fusb302.c @@ -16,7 +16,6 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of_device.h> -#include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/pinctrl/consumer.h> #include <linux/power_supply.h> @@ -1543,6 +1542,21 @@ static int fusb302_pd_read_message(struct fusb302_chip *chip, fusb302_log(chip, "PD message header: %x", msg->header); fusb302_log(chip, "PD message len: %d", len); + /* + * Check if we've read off a GoodCRC message. If so then indicate to + * TCPM that the previous transmission has completed. Otherwise we pass + * the received message over to TCPM for processing. + * + * We make this check here instead of basing the reporting decision on + * the IRQ event type, as it's possible for the chip to report the + * TX_SUCCESS and GCRCSENT events out of order on occasion, so we need + * to check the message type to ensure correct reporting to TCPM. + */ + if ((!len) && (pd_header_type_le(msg->header) == PD_CTRL_GOOD_CRC)) + tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS); + else + tcpm_pd_receive(chip->tcpm_port, msg); + return ret; } @@ -1650,13 +1664,12 @@ static irqreturn_t fusb302_irq_intn(int irq, void *dev_id) if (interrupta & FUSB_REG_INTERRUPTA_TX_SUCCESS) { fusb302_log(chip, "IRQ: PD tx success"); - /* read out the received good CRC */ ret = fusb302_pd_read_message(chip, &pd_msg); if (ret < 0) { - fusb302_log(chip, "cannot read in GCRC, ret=%d", ret); + fusb302_log(chip, + "cannot read in PD message, ret=%d", ret); goto done; } - tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS); } if (interrupta & FUSB_REG_INTERRUPTA_HARDRESET) { @@ -1677,7 +1690,6 @@ static irqreturn_t fusb302_irq_intn(int irq, void *dev_id) "cannot read in PD message, ret=%d", ret); goto done; } - tcpm_pd_receive(chip->tcpm_port, &pd_msg); } done: mutex_unlock(&chip->lock); @@ -1731,24 +1743,24 @@ static int init_gpio(struct fusb302_chip *chip) chip->gpio_int_n = of_get_named_gpio(node, "fcs,int_n", 0); if (!gpio_is_valid(chip->gpio_int_n)) { ret = chip->gpio_int_n; - fusb302_log(chip, "cannot get named GPIO Int_N, ret=%d", ret); + dev_err(chip->dev, "cannot get named GPIO Int_N, ret=%d", ret); return ret; } ret = devm_gpio_request(chip->dev, chip->gpio_int_n, "fcs,int_n"); if (ret < 0) { - fusb302_log(chip, "cannot request GPIO Int_N, ret=%d", ret); + dev_err(chip->dev, "cannot request GPIO Int_N, ret=%d", ret); return ret; } ret = gpio_direction_input(chip->gpio_int_n); if (ret < 0) { - fusb302_log(chip, - "cannot set GPIO Int_N to input, ret=%d", ret); + dev_err(chip->dev, + "cannot set GPIO Int_N to input, ret=%d", ret); return ret; } ret = gpio_to_irq(chip->gpio_int_n); if (ret < 0) { - fusb302_log(chip, - "cannot request IRQ for GPIO Int_N, ret=%d", ret); + dev_err(chip->dev, + "cannot request IRQ for GPIO Int_N, ret=%d", ret); return ret; } chip->gpio_int_n_irq = ret; @@ -1845,7 +1857,7 @@ static int fusb302_probe(struct i2c_client *client, chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev); if (IS_ERR(chip->tcpm_port)) { ret = PTR_ERR(chip->tcpm_port); - fusb302_log(chip, "cannot register tcpm port, ret=%d", ret); + dev_err(dev, "cannot register tcpm port, ret=%d", ret); goto destroy_workqueue; } @@ -1854,8 +1866,7 @@ static int fusb302_probe(struct i2c_client *client, IRQF_ONESHOT | IRQF_TRIGGER_LOW, "fsc_interrupt_int_n", chip); if (ret < 0) { - fusb302_log(chip, - "cannot request IRQ for GPIO Int_N, ret=%d", ret); + dev_err(dev, "cannot request IRQ for GPIO Int_N, ret=%d", ret); goto tcpm_unregister_port; } enable_irq_wake(chip->gpio_int_n_irq); diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index c166fc77dfb8..f4d563ee7690 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -252,6 +252,9 @@ struct tcpm_port { unsigned int nr_src_pdo; u32 snk_pdo[PDO_MAX_OBJECTS]; unsigned int nr_snk_pdo; + unsigned int nr_fixed; /* number of fixed sink PDOs */ + unsigned int nr_var; /* number of variable sink PDOs */ + unsigned int nr_batt; /* number of battery sink PDOs */ u32 snk_vdo[VDO_MAX_OBJECTS]; unsigned int nr_snk_vdo; @@ -1247,6 +1250,100 @@ static void vdm_state_machine_work(struct work_struct *work) mutex_unlock(&port->lock); } +enum pdo_err { + PDO_NO_ERR, + PDO_ERR_NO_VSAFE5V, + PDO_ERR_VSAFE5V_NOT_FIRST, + PDO_ERR_PDO_TYPE_NOT_IN_ORDER, + PDO_ERR_FIXED_NOT_SORTED, + PDO_ERR_VARIABLE_BATT_NOT_SORTED, + PDO_ERR_DUPE_PDO, +}; + +static const char * const pdo_err_msg[] = { + [PDO_ERR_NO_VSAFE5V] = + " err: source/sink caps should atleast have vSafe5V", + [PDO_ERR_VSAFE5V_NOT_FIRST] = + " err: vSafe5V Fixed Supply Object Shall always be the first object", + [PDO_ERR_PDO_TYPE_NOT_IN_ORDER] = + " err: PDOs should be in the following order: Fixed; Battery; Variable", + [PDO_ERR_FIXED_NOT_SORTED] = + " err: Fixed supply pdos should be in increasing order of their fixed voltage", + [PDO_ERR_VARIABLE_BATT_NOT_SORTED] = + " err: Variable/Battery supply pdos should be in increasing order of their minimum voltage", + [PDO_ERR_DUPE_PDO] = + " err: Variable/Batt supply pdos cannot have same min/max voltage", +}; + +static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) +{ + unsigned int i; + + /* Should at least contain vSafe5v */ + if (nr_pdo < 1) + return PDO_ERR_NO_VSAFE5V; + + /* The vSafe5V Fixed Supply Object Shall always be the first object */ + if (pdo_type(pdo[0]) != PDO_TYPE_FIXED || + pdo_fixed_voltage(pdo[0]) != VSAFE5V) + return PDO_ERR_VSAFE5V_NOT_FIRST; + + for (i = 1; i < nr_pdo; i++) { + if (pdo_type(pdo[i]) < pdo_type(pdo[i - 1])) { + return PDO_ERR_PDO_TYPE_NOT_IN_ORDER; + } else if (pdo_type(pdo[i]) == pdo_type(pdo[i - 1])) { + enum pd_pdo_type type = pdo_type(pdo[i]); + + switch (type) { + /* + * The remaining Fixed Supply Objects, if + * present, shall be sent in voltage order; + * lowest to highest. + */ + case PDO_TYPE_FIXED: + if (pdo_fixed_voltage(pdo[i]) <= + pdo_fixed_voltage(pdo[i - 1])) + return PDO_ERR_FIXED_NOT_SORTED; + break; + /* + * The Battery Supply Objects and Variable + * supply, if present shall be sent in Minimum + * Voltage order; lowest to highest. + */ + case PDO_TYPE_VAR: + case PDO_TYPE_BATT: + if (pdo_min_voltage(pdo[i]) < + pdo_min_voltage(pdo[i - 1])) + return PDO_ERR_VARIABLE_BATT_NOT_SORTED; + else if ((pdo_min_voltage(pdo[i]) == + pdo_min_voltage(pdo[i - 1])) && + (pdo_max_voltage(pdo[i]) == + pdo_min_voltage(pdo[i - 1]))) + return PDO_ERR_DUPE_PDO; + break; + default: + tcpm_log_force(port, " Unknown pdo type"); + } + } + } + + return PDO_NO_ERR; +} + +static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) +{ + enum pdo_err err_index = tcpm_caps_err(port, pdo, nr_pdo); + + if (err_index != PDO_NO_ERR) { + tcpm_log_force(port, " %s", pdo_err_msg[err_index]); + return -EINVAL; + } + + return 0; +} + /* * PD (data, control) command handling functions */ @@ -1269,6 +1366,9 @@ static void tcpm_pd_data_request(struct tcpm_port *port, tcpm_log_source_caps(port); + tcpm_validate_caps(port, port->source_caps, + port->nr_source_caps); + /* * This message may be received even if VBUS is not * present. This is quite unexpected; see USB PD @@ -1670,39 +1770,90 @@ static int tcpm_pd_check_request(struct tcpm_port *port) return 0; } -static int tcpm_pd_select_pdo(struct tcpm_port *port) +#define min_power(x, y) min(pdo_max_power(x), pdo_max_power(y)) +#define min_current(x, y) min(pdo_max_current(x), pdo_max_current(y)) + +static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, + int *src_pdo) { - unsigned int i, max_mw = 0, max_mv = 0; + unsigned int i, j, max_mw = 0, max_mv = 0, mw = 0, mv = 0, ma = 0; int ret = -EINVAL; /* - * Select the source PDO providing the most power while staying within - * the board's voltage limits. Prefer PDO providing exp + * Select the source PDO providing the most power which has a + * matchig sink cap. */ for (i = 0; i < port->nr_source_caps; i++) { u32 pdo = port->source_caps[i]; enum pd_pdo_type type = pdo_type(pdo); - unsigned int mv, ma, mw; - - if (type == PDO_TYPE_FIXED) - mv = pdo_fixed_voltage(pdo); - else - mv = pdo_min_voltage(pdo); - - if (type == PDO_TYPE_BATT) { - mw = pdo_max_power(pdo); - } else { - ma = min(pdo_max_current(pdo), - port->max_snk_ma); - mw = ma * mv / 1000; - } - /* Perfer higher voltages if available */ - if ((mw > max_mw || (mw == max_mw && mv > max_mv)) && - mv <= port->max_snk_mv) { - ret = i; - max_mw = mw; - max_mv = mv; + if (type == PDO_TYPE_FIXED) { + for (j = 0; j < port->nr_fixed; j++) { + if (pdo_fixed_voltage(pdo) == + pdo_fixed_voltage(port->snk_pdo[j])) { + ma = min_current(pdo, port->snk_pdo[j]); + mv = pdo_fixed_voltage(pdo); + mw = ma * mv / 1000; + if (mw > max_mw || + (mw == max_mw && mv > max_mv)) { + ret = 0; + *src_pdo = i; + *sink_pdo = j; + max_mw = mw; + max_mv = mv; + } + /* There could only be one fixed pdo + * at a specific voltage level. + * So breaking here. + */ + break; + } + } + } else if (type == PDO_TYPE_BATT) { + for (j = port->nr_fixed; + j < port->nr_fixed + + port->nr_batt; + j++) { + if (pdo_min_voltage(pdo) >= + pdo_min_voltage(port->snk_pdo[j]) && + pdo_max_voltage(pdo) <= + pdo_max_voltage(port->snk_pdo[j])) { + mw = min_power(pdo, port->snk_pdo[j]); + mv = pdo_min_voltage(pdo); + if (mw > max_mw || + (mw == max_mw && mv > max_mv)) { + ret = 0; + *src_pdo = i; + *sink_pdo = j; + max_mw = mw; + max_mv = mv; + } + } + } + } else if (type == PDO_TYPE_VAR) { + for (j = port->nr_fixed + + port->nr_batt; + j < port->nr_fixed + + port->nr_batt + + port->nr_var; + j++) { + if (pdo_min_voltage(pdo) >= + pdo_min_voltage(port->snk_pdo[j]) && + pdo_max_voltage(pdo) <= + pdo_max_voltage(port->snk_pdo[j])) { + ma = min_current(pdo, port->snk_pdo[j]); + mv = pdo_min_voltage(pdo); + mw = ma * mv / 1000; + if (mw > max_mw || + (mw == max_mw && mv > max_mv)) { + ret = 0; + *src_pdo = i; + *sink_pdo = j; + max_mw = mw; + max_mv = mv; + } + } + } } } @@ -1714,13 +1865,14 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) unsigned int mv, ma, mw, flags; unsigned int max_ma, max_mw; enum pd_pdo_type type; - int index; - u32 pdo; + int src_pdo_index, snk_pdo_index; + u32 pdo, matching_snk_pdo; - index = tcpm_pd_select_pdo(port); - if (index < 0) + if (tcpm_pd_select_pdo(port, &snk_pdo_index, &src_pdo_index) < 0) return -EINVAL; - pdo = port->source_caps[index]; + + pdo = port->source_caps[src_pdo_index]; + matching_snk_pdo = port->snk_pdo[snk_pdo_index]; type = pdo_type(pdo); if (type == PDO_TYPE_FIXED) @@ -1728,26 +1880,28 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) else mv = pdo_min_voltage(pdo); - /* Select maximum available current within the board's power limit */ + /* Select maximum available current within the sink pdo's limit */ if (type == PDO_TYPE_BATT) { - mw = pdo_max_power(pdo); - ma = 1000 * min(mw, port->max_snk_mw) / mv; + mw = min_power(pdo, matching_snk_pdo); + ma = 1000 * mw / mv; } else { - ma = min(pdo_max_current(pdo), - 1000 * port->max_snk_mw / mv); + ma = min_current(pdo, matching_snk_pdo); + mw = ma * mv / 1000; } - ma = min(ma, port->max_snk_ma); flags = RDO_USB_COMM | RDO_NO_SUSPEND; /* Set mismatch bit if offered power is less than operating power */ - mw = ma * mv / 1000; max_ma = ma; max_mw = mw; if (mw < port->operating_snk_mw) { flags |= RDO_CAP_MISMATCH; - max_mw = port->operating_snk_mw; - max_ma = max_mw * 1000 / mv; + if (type == PDO_TYPE_BATT && + (pdo_max_power(matching_snk_pdo) > pdo_max_power(pdo))) + max_mw = pdo_max_power(matching_snk_pdo); + else if (pdo_max_current(matching_snk_pdo) > + pdo_max_current(pdo)) + max_ma = pdo_max_current(matching_snk_pdo); } tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d", @@ -1756,16 +1910,16 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) port->polarity); if (type == PDO_TYPE_BATT) { - *rdo = RDO_BATT(index + 1, mw, max_mw, flags); + *rdo = RDO_BATT(src_pdo_index + 1, mw, max_mw, flags); tcpm_log(port, "Requesting PDO %d: %u mV, %u mW%s", - index, mv, mw, + src_pdo_index, mv, mw, flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); } else { - *rdo = RDO_FIXED(index + 1, ma, max_ma, flags); + *rdo = RDO_FIXED(src_pdo_index + 1, ma, max_ma, flags); tcpm_log(port, "Requesting PDO %d: %u mV, %u mA%s", - index, mv, ma, + src_pdo_index, mv, ma, flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); } @@ -3435,9 +3589,12 @@ static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo, return nr_vdo; } -void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo) +int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) { + if (tcpm_validate_caps(port, pdo, nr_pdo)) + return -EINVAL; + mutex_lock(&port->lock); port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, pdo, nr_pdo); switch (port->state) { @@ -3457,16 +3614,20 @@ void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, break; } mutex_unlock(&port->lock); + return 0; } EXPORT_SYMBOL_GPL(tcpm_update_source_capabilities); -void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo, - unsigned int max_snk_mv, - unsigned int max_snk_ma, - unsigned int max_snk_mw, - unsigned int operating_snk_mw) +int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo, + unsigned int max_snk_mv, + unsigned int max_snk_ma, + unsigned int max_snk_mw, + unsigned int operating_snk_mw) { + if (tcpm_validate_caps(port, pdo, nr_pdo)) + return -EINVAL; + mutex_lock(&port->lock); port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo); port->max_snk_mv = max_snk_mv; @@ -3485,9 +3646,23 @@ void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, break; } mutex_unlock(&port->lock); + return 0; } EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities); +static int nr_type_pdos(const u32 *pdo, unsigned int nr_pdo, + enum pd_pdo_type type) +{ + int count = 0; + int i; + + for (i = 0; i < nr_pdo; i++) { + if (pdo_type(pdo[i]) == type) + count++; + } + return count; +} + struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) { struct tcpm_port *port; @@ -3520,11 +3695,28 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) init_completion(&port->tx_complete); init_completion(&port->swap_complete); + tcpm_debugfs_init(port); + if (tcpm_validate_caps(port, tcpc->config->src_pdo, + tcpc->config->nr_src_pdo) || + tcpm_validate_caps(port, tcpc->config->snk_pdo, + tcpc->config->nr_snk_pdo)) { + err = -EINVAL; + goto out_destroy_wq; + } port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo, tcpc->config->nr_src_pdo); port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo, tcpc->config->nr_snk_pdo); + port->nr_fixed = nr_type_pdos(port->snk_pdo, + port->nr_snk_pdo, + PDO_TYPE_FIXED); + port->nr_var = nr_type_pdos(port->snk_pdo, + port->nr_snk_pdo, + PDO_TYPE_VAR); + port->nr_batt = nr_type_pdos(port->snk_pdo, + port->nr_snk_pdo, + PDO_TYPE_BATT); port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcpc->config->snk_vdo, tcpc->config->nr_snk_vdo); @@ -3575,7 +3767,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) } } - tcpm_debugfs_init(port); mutex_lock(&port->lock); tcpm_init(port); mutex_unlock(&port->lock); diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c index a8d479eb221a..2e990e0d917d 100644 --- a/drivers/usb/typec/typec_wcove.c +++ b/drivers/usb/typec/typec_wcove.c @@ -556,10 +556,8 @@ static const u32 src_pdo[] = { }; static const u32 snk_pdo[] = { - PDO_FIXED(12000, 3000, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | + PDO_FIXED(5000, 500, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM), - PDO_BATT(4750, 12000, 15000), - PDO_VAR(4750, 12000, 3000), }; static struct tcpc_config wcove_typec_config = { diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index d0c31cee4720..e36d6c73c4a4 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -1,7 +1,6 @@ config TYPEC_UCSI tristate "USB Type-C Connector System Software Interface driver" depends on !CPU_BIG_ENDIAN - select TYPEC help USB Type-C Connector System Software Interface (UCSI) is a specification for an interface that allows the operating system to diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c index a3df8ee82faf..e31a6f204397 100644 --- a/drivers/usb/usbip/stub_dev.c +++ b/drivers/usb/usbip/stub_dev.c @@ -149,8 +149,7 @@ static void stub_shutdown_connection(struct usbip_device *ud) * step 1? */ if (ud->tcp_socket) { - dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n", - ud->tcp_socket); + dev_dbg(&sdev->udev->dev, "shutdown sockfd %d\n", ud->sockfd); kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); } diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c index 4f48b306713f..c31c8402a0c5 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -237,11 +237,12 @@ void stub_device_cleanup_urbs(struct stub_device *sdev) struct stub_priv *priv; struct urb *urb; - dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev); + dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n"); while ((priv = stub_priv_pop(sdev))) { urb = priv->urb; - dev_dbg(&sdev->udev->dev, "free urb %p\n", urb); + dev_dbg(&sdev->udev->dev, "free urb seqnum %lu\n", + priv->seqnum); usb_kill_urb(urb); kmem_cache_free(stub_priv_cache, priv); diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 536e037f541f..97b09a42a10c 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -211,9 +211,6 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev, if (priv->seqnum != pdu->u.cmd_unlink.seqnum) continue; - dev_info(&priv->urb->dev->dev, "unlink urb %p\n", - priv->urb); - /* * This matched urb is not completed yet (i.e., be in * flight in usb hcd hardware/driver). Now we are @@ -252,8 +249,8 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev, ret = usb_unlink_urb(priv->urb); if (ret != -EINPROGRESS) dev_err(&priv->urb->dev->dev, - "failed to unlink a urb %p, ret %d\n", - priv->urb, ret); + "failed to unlink a urb # %lu, ret %d\n", + priv->seqnum, ret); return 0; } @@ -322,23 +319,26 @@ static struct stub_priv *stub_priv_alloc(struct stub_device *sdev, return priv; } -static int get_pipe(struct stub_device *sdev, int epnum, int dir) +static int get_pipe(struct stub_device *sdev, struct usbip_header *pdu) { struct usb_device *udev = sdev->udev; struct usb_host_endpoint *ep; struct usb_endpoint_descriptor *epd = NULL; + int epnum = pdu->base.ep; + int dir = pdu->base.direction; + + if (epnum < 0 || epnum > 15) + goto err_ret; if (dir == USBIP_DIR_IN) ep = udev->ep_in[epnum & 0x7f]; else ep = udev->ep_out[epnum & 0x7f]; - if (!ep) { - dev_err(&sdev->udev->dev, "no such endpoint?, %d\n", - epnum); - BUG(); - } + if (!ep) + goto err_ret; epd = &ep->desc; + if (usb_endpoint_xfer_control(epd)) { if (dir == USBIP_DIR_OUT) return usb_sndctrlpipe(udev, epnum); @@ -361,15 +361,31 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) } if (usb_endpoint_xfer_isoc(epd)) { + /* validate packet size and number of packets */ + unsigned int maxp, packets, bytes; + + maxp = usb_endpoint_maxp(epd); + maxp *= usb_endpoint_maxp_mult(epd); + bytes = pdu->u.cmd_submit.transfer_buffer_length; + packets = DIV_ROUND_UP(bytes, maxp); + + if (pdu->u.cmd_submit.number_of_packets < 0 || + pdu->u.cmd_submit.number_of_packets > packets) { + dev_err(&sdev->udev->dev, + "CMD_SUBMIT: isoc invalid num packets %d\n", + pdu->u.cmd_submit.number_of_packets); + return -1; + } if (dir == USBIP_DIR_OUT) return usb_sndisocpipe(udev, epnum); else return usb_rcvisocpipe(udev, epnum); } +err_ret: /* NOT REACHED */ - dev_err(&sdev->udev->dev, "get pipe, epnum %d\n", epnum); - return 0; + dev_err(&sdev->udev->dev, "CMD_SUBMIT: invalid epnum %d\n", epnum); + return -1; } static void masking_bogus_flags(struct urb *urb) @@ -412,9 +428,6 @@ static void masking_bogus_flags(struct urb *urb) if (is_out) allowed |= URB_ZERO_PACKET; /* FALLTHROUGH */ - case USB_ENDPOINT_XFER_CONTROL: - allowed |= URB_NO_FSBR; /* only affects UHCI */ - /* FALLTHROUGH */ default: /* all non-iso endpoints */ if (!is_out) allowed |= URB_SHORT_NOT_OK; @@ -433,7 +446,10 @@ static void stub_recv_cmd_submit(struct stub_device *sdev, struct stub_priv *priv; struct usbip_device *ud = &sdev->ud; struct usb_device *udev = sdev->udev; - int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); + int pipe = get_pipe(sdev, pdu); + + if (pipe == -1) + return; priv = stub_priv_alloc(sdev, pdu); if (!priv) diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index b18bce96c212..f0ec41a50cbc 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -88,7 +88,7 @@ void stub_complete(struct urb *urb) /* link a urb to the queue of tx. */ spin_lock_irqsave(&sdev->priv_lock, flags); if (sdev->ud.tcp_socket == NULL) { - usbip_dbg_stub_tx("ignore urb for closed connection %p", urb); + usbip_dbg_stub_tx("ignore urb for closed connection\n"); /* It will be freed in stub_device_cleanup_urbs(). */ } else if (priv->unlinking) { stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status); @@ -167,6 +167,13 @@ static int stub_send_ret_submit(struct stub_device *sdev) memset(&pdu_header, 0, sizeof(pdu_header)); memset(&msg, 0, sizeof(msg)); + if (urb->actual_length > 0 && !urb->transfer_buffer) { + dev_err(&sdev->udev->dev, + "urb: actual_length %d transfer_buffer null\n", + urb->actual_length); + return -1; + } + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) iovnum = 2 + urb->number_of_packets; else @@ -183,8 +190,8 @@ static int stub_send_ret_submit(struct stub_device *sdev) /* 1. setup usbip_header */ setup_ret_submit_pdu(&pdu_header, urb); - usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", - pdu_header.base.seqnum, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d\n", + pdu_header.base.seqnum); usbip_header_correct_endian(&pdu_header, 1); iov[iovnum].iov_base = &pdu_header; diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index f7978933b402..b5af6fc7169b 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -317,26 +317,19 @@ int usbip_recv(struct socket *sock, void *buf, int size) struct msghdr msg = {.msg_flags = MSG_NOSIGNAL}; int total = 0; + if (!sock || !buf || !size) + return -EINVAL; + iov_iter_kvec(&msg.msg_iter, READ|ITER_KVEC, &iov, 1, size); usbip_dbg_xmit("enter\n"); - if (!sock || !buf || !size) { - pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf, - size); - return -EINVAL; - } - do { - int sz = msg_data_left(&msg); sock->sk->sk_allocation = GFP_NOIO; result = sock_recvmsg(sock, &msg, MSG_WAITALL); - if (result <= 0) { - pr_debug("receive sock %p buf %p size %u ret %d total %d\n", - sock, buf + total, sz, result, total); + if (result <= 0) goto err; - } total += result; } while (msg_data_left(&msg)); diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index e5de35c8c505..473fb8a87289 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -256,6 +256,7 @@ struct usbip_device { /* lock for status */ spinlock_t lock; + int sockfd; struct socket *tcp_socket; struct task_struct *tcp_rx; diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 713e94170963..c3e1008aa491 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -656,9 +656,6 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag struct vhci_device *vdev; unsigned long flags; - usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", - hcd, urb, mem_flags); - if (portnum > VHCI_HC_PORTS) { pr_err("invalid port number %d\n", portnum); return -ENODEV; @@ -822,8 +819,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) struct vhci_device *vdev; unsigned long flags; - pr_info("dequeue a urb %p\n", urb); - spin_lock_irqsave(&vhci->lock, flags); priv = urb->hcpriv; @@ -851,7 +846,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) /* tcp connection is closed */ spin_lock(&vdev->priv_lock); - pr_info("device %p seems to be disconnected\n", vdev); list_del(&priv->list); kfree(priv); urb->hcpriv = NULL; @@ -863,8 +857,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) * vhci_rx will receive RET_UNLINK and give back the URB. * Otherwise, we give back it here. */ - pr_info("gives back urb %p\n", urb); - usb_hcd_unlink_urb_from_ep(hcd, urb); spin_unlock_irqrestore(&vhci->lock, flags); @@ -892,8 +884,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) unlink->unlink_seqnum = priv->seqnum; - pr_info("device %p seems to be still connected\n", vdev); - /* send cmd_unlink and try to cancel the pending URB in the * peer */ list_add_tail(&unlink->list, &vdev->unlink_tx); @@ -975,7 +965,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) /* need this? see stub_dev.c */ if (ud->tcp_socket) { - pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket); + pr_debug("shutdown tcp_socket %d\n", ud->sockfd); kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); } @@ -1098,7 +1088,6 @@ static int hcd_name_to_id(const char *name) static int vhci_setup(struct usb_hcd *hcd) { struct vhci *vhci = *((void **)dev_get_platdata(hcd->self.controller)); - hcd->self.sg_tablesize = ~0; if (usb_hcd_is_primary_hcd(hcd)) { vhci->vhci_hcd_hs = hcd_to_vhci_hcd(hcd); vhci->vhci_hcd_hs->vhci = vhci; diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index 90577e8b2282..44cd64518925 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -23,24 +23,23 @@ struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum) urb = priv->urb; status = urb->status; - usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n", - urb, priv, seqnum); + usbip_dbg_vhci_rx("find urb seqnum %u\n", seqnum); switch (status) { case -ENOENT: /* fall through */ case -ECONNRESET: - dev_info(&urb->dev->dev, - "urb %p was unlinked %ssynchronuously.\n", urb, - status == -ENOENT ? "" : "a"); + dev_dbg(&urb->dev->dev, + "urb seq# %u was unlinked %ssynchronously\n", + seqnum, status == -ENOENT ? "" : "a"); break; case -EINPROGRESS: /* no info output */ break; default: - dev_info(&urb->dev->dev, - "urb %p may be in a error, status %d\n", urb, - status); + dev_dbg(&urb->dev->dev, + "urb seq# %u may be in a error, status %d\n", + seqnum, status); } list_del(&priv->list); @@ -67,8 +66,8 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, spin_unlock_irqrestore(&vdev->priv_lock, flags); if (!urb) { - pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum); - pr_info("max seqnum %d\n", + pr_err("cannot find a urb of seqnum %u max seqnum %d\n", + pdu->base.seqnum, atomic_read(&vhci_hcd->seqnum)); usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); return; @@ -91,7 +90,7 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, if (usbip_dbg_flag_vhci_rx) usbip_dump_urb(urb); - usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum); spin_lock_irqsave(&vhci->lock, flags); usb_hcd_unlink_urb_from_ep(vhci_hcd_to_hcd(vhci_hcd), urb); @@ -158,7 +157,7 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, pr_info("the urb (seqnum %d) was already given back\n", pdu->base.seqnum); } else { - usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + usbip_dbg_vhci_rx("now giveback urb %d\n", pdu->base.seqnum); /* If unlink is successful, status is -ECONNRESET */ urb->status = pdu->u.ret_unlink.status; diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index e78f7472cac4..091f76b7196d 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -17,15 +17,20 @@ /* * output example: - * hub port sta spd dev socket local_busid - * hs 0000 004 000 00000000 c5a7bb80 1-2.3 + * hub port sta spd dev sockfd local_busid + * hs 0000 004 000 00000000 3 1-2.3 * ................................................ - * ss 0008 004 000 00000000 d8cee980 2-3.4 + * ss 0008 004 000 00000000 4 2-3.4 * ................................................ * - * IP address can be retrieved from a socket pointer address by looking - * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a - * port number and its peer IP address. + * Output includes socket fd instead of socket pointer address to avoid + * leaking kernel memory address in: + * /sys/devices/platform/vhci_hcd.0/status and in debug output. + * The socket pointer address is not used at the moment and it was made + * visible as a convenient way to find IP address from socket pointer + * address by looking up /proc/net/{tcp,tcp6}. As this opens a security + * hole, the change is made to use sockfd instead. + * */ static void port_show_vhci(char **out, int hub, int port, struct vhci_device *vdev) { @@ -39,8 +44,8 @@ static void port_show_vhci(char **out, int hub, int port, struct vhci_device *vd if (vdev->ud.status == VDEV_ST_USED) { *out += sprintf(*out, "%03u %08x ", vdev->speed, vdev->devid); - *out += sprintf(*out, "%16p %s", - vdev->ud.tcp_socket, + *out += sprintf(*out, "%u %s", + vdev->ud.sockfd, dev_name(&vdev->udev->dev)); } else { @@ -160,7 +165,8 @@ static ssize_t nports_show(struct device *dev, struct device_attribute *attr, char *s = out; /* - * Half the ports are for SPEED_HIGH and half for SPEED_SUPER, thus the * 2. + * Half the ports are for SPEED_HIGH and half for SPEED_SUPER, + * thus the * 2. */ out += sprintf(out, "%d\n", VHCI_PORTS * vhci_num_controllers); return out - s; @@ -366,6 +372,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, vdev->devid = devid; vdev->speed = speed; + vdev->ud.sockfd = sockfd; vdev->ud.tcp_socket = socket; vdev->ud.status = VDEV_ST_NOTASSIGNED; diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c index d625a2ff4b71..9aed15a358b7 100644 --- a/drivers/usb/usbip/vhci_tx.c +++ b/drivers/usb/usbip/vhci_tx.c @@ -69,7 +69,8 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev) memset(&msg, 0, sizeof(msg)); memset(&iov, 0, sizeof(iov)); - usbip_dbg_vhci_tx("setup txdata urb %p\n", urb); + usbip_dbg_vhci_tx("setup txdata urb seqnum %lu\n", + priv->seqnum); /* 1. setup usbip_header */ setup_cmd_submit_pdu(&pdu_header, urb); |