diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-01-18 12:09:13 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-01-18 12:09:13 -0800 |
| commit | 27983960f0fb7331d3635c1f0270b794cc6b930c (patch) | |
| tree | 60e9d2aa78418e7eba3d6f6f08a338fef3ac3f80 | |
| parent | 5d1765a96c66360bbbec5377ce7344d286715a8a (diff) | |
| parent | dd83dc1249737b837ac5d57c81f2b0977c613d9f (diff) | |
Merge tag 'usb-6.19-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB fixes from Greg KH:
"Here are some small USB fixes and new device ids for 6.19-rc6
Included in here are:
- new usb-serial device ids
- dwc3-apple driver fixes to get things working properly on that
hardware platform
- ohci/uhci platfrom driver module soft-deps with ehci to remove a
runtime warning that sometimes shows up on some platforms.
- quirk for broken devices that can not handle reading the BOS
descriptor from them without going crazy.
- usb-serial driver fixes
- xhci driver fixes
- usb gadget driver fixes
All of these except for the last xhci fix has been in linux-next for a
while. The xhci fix has been reported by others to solve the issue for
them, so should be ok"
* tag 'usb-6.19-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb:
xhci: sideband: don't dereference freed ring when removing sideband endpoint
usb: gadget: uvc: retry vb2_reqbufs() with vb_vmalloc_memops if use_sg fail
usb: gadget: uvc: return error from uvcg_queue_init()
usb: gadget: uvc: fix interval_duration calculation
usb: gadget: uvc: fix req_payload_size calculation
usb: dwc3: apple: Ignore USB role switches to the active role
usb: host: xhci-tegra: Use platform_get_irq_optional() for wake IRQs
USB: OHCI/UHCI: Add soft dependencies on ehci_platform
usb: dwc3: apple: Set USB2 PHY mode before dwc3 init
USB: serial: f81232: fix incomplete serial port generation
USB: serial: ftdi_sio: add support for PICAXE AXE027 cable
USB: serial: option: add Telit LE910 MBIM composition
usb: core: add USB_QUIRK_NO_BOS for devices that hang on BOS descriptor
dt-bindings: usb: qcom,dwc3: Correct MSM8994 interrupts
dt-bindings: usb: qcom,dwc3: Correct IPQ5018 interrupts
tcpm: allow looking for role_sw device in the main node
usb: dwc3: Check for USB4 IP_NAME
| -rw-r--r-- | Documentation/devicetree/bindings/usb/qcom,dwc3.yaml | 4 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml | 4 | ||||
| -rw-r--r-- | drivers/usb/core/config.c | 5 | ||||
| -rw-r--r-- | drivers/usb/core/quirks.c | 3 | ||||
| -rw-r--r-- | drivers/usb/dwc3/core.c | 2 | ||||
| -rw-r--r-- | drivers/usb/dwc3/core.h | 1 | ||||
| -rw-r--r-- | drivers/usb/dwc3/dwc3-apple.c | 64 | ||||
| -rw-r--r-- | drivers/usb/gadget/function/f_uvc.c | 4 | ||||
| -rw-r--r-- | drivers/usb/gadget/function/uvc.h | 3 | ||||
| -rw-r--r-- | drivers/usb/gadget/function/uvc_queue.c | 23 | ||||
| -rw-r--r-- | drivers/usb/gadget/function/uvc_video.c | 14 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-platform.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-platform.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-sideband.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-tegra.c | 2 | ||||
| -rw-r--r-- | drivers/usb/host/xhci.c | 15 | ||||
| -rw-r--r-- | drivers/usb/serial/f81232.c | 77 | ||||
| -rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 1 | ||||
| -rw-r--r-- | drivers/usb/serial/ftdi_sio_ids.h | 2 | ||||
| -rw-r--r-- | drivers/usb/serial/option.c | 1 | ||||
| -rw-r--r-- | drivers/usb/typec/tcpm/tcpm.c | 2 | ||||
| -rw-r--r-- | include/linux/usb/quirks.h | 3 |
22 files changed, 166 insertions, 67 deletions
diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index a792434c59db..a7f58114c02e 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -406,7 +406,6 @@ allOf: compatible: contains: enum: - - qcom,ipq5018-dwc3 - qcom,ipq6018-dwc3 - qcom,ipq8074-dwc3 - qcom,msm8953-dwc3 @@ -428,6 +427,7 @@ allOf: compatible: contains: enum: + - qcom,msm8994-dwc3 - qcom,msm8996-dwc3 - qcom,qcs404-dwc3 - qcom,sdm660-dwc3 @@ -451,6 +451,7 @@ allOf: compatible: contains: enum: + - qcom,ipq5018-dwc3 - qcom,ipq5332-dwc3 then: properties: @@ -488,7 +489,6 @@ allOf: enum: - qcom,ipq4019-dwc3 - qcom,ipq8064-dwc3 - - qcom,msm8994-dwc3 - qcom,qcs615-dwc3 - qcom,qcs8300-dwc3 - qcom,qdu1000-dwc3 diff --git a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml index 8cee7c5582f2..7d784a648b7d 100644 --- a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml @@ -420,7 +420,6 @@ allOf: compatible: contains: enum: - - qcom,ipq5018-dwc3 - qcom,ipq6018-dwc3 - qcom,ipq8074-dwc3 - qcom,msm8953-dwc3 @@ -443,6 +442,7 @@ allOf: compatible: contains: enum: + - qcom,msm8994-dwc3 - qcom,msm8996-dwc3 - qcom,qcs404-dwc3 - qcom,sdm660-dwc3 @@ -467,6 +467,7 @@ allOf: compatible: contains: enum: + - qcom,ipq5018-dwc3 - qcom,ipq5332-dwc3 then: properties: @@ -509,7 +510,6 @@ allOf: - qcom,ipq4019-dwc3 - qcom,ipq8064-dwc3 - qcom,kaanapali-dwc3 - - qcom,msm8994-dwc3 - qcom,qcs615-dwc3 - qcom,qcs8300-dwc3 - qcom,qdu1000-dwc3 diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index baf5bc844b6f..2bb1ceb9d621 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -1040,6 +1040,11 @@ int usb_get_bos_descriptor(struct usb_device *dev) __u8 cap_type; int ret; + if (dev->quirks & USB_QUIRK_NO_BOS) { + dev_dbg(ddev, "skipping BOS descriptor\n"); + return -ENOMSG; + } + bos = kzalloc(sizeof(*bos), GFP_KERNEL); if (!bos) return -ENOMEM; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 47f589c4104a..c4d85089d19b 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -450,6 +450,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x0c45, 0x7056), .driver_info = USB_QUIRK_IGNORE_REMOTE_WAKEUP }, + /* Elgato 4K X - BOS descriptor fetch hangs at SuperSpeed Plus */ + { USB_DEVICE(0x0fd9, 0x009b), .driver_info = USB_QUIRK_NO_BOS }, + /* Sony Xperia XZ1 Compact (lilac) smartphone in fastboot mode */ { USB_DEVICE(0x0fce, 0x0dde), .driver_info = USB_QUIRK_NO_LPM }, diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ec8407972b9d..93fd5fdf95cb 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -993,6 +993,8 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); dwc->ip = DWC3_GSNPS_ID(reg); + if (dwc->ip == DWC4_IP) + dwc->ip = DWC32_IP; /* This should read as U3 followed by revision number */ if (DWC3_IP_IS(DWC3)) { diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a5fc92c4ffa3..45757169b672 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1265,6 +1265,7 @@ struct dwc3 { #define DWC3_IP 0x5533 #define DWC31_IP 0x3331 #define DWC32_IP 0x3332 +#define DWC4_IP 0x3430 u32 revision; diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c index cc47cad232e3..40c3ccfddb67 100644 --- a/drivers/usb/dwc3/dwc3-apple.c +++ b/drivers/usb/dwc3/dwc3-apple.c @@ -218,25 +218,31 @@ static int dwc3_apple_core_init(struct dwc3_apple *appledwc) return ret; } -static void dwc3_apple_phy_set_mode(struct dwc3_apple *appledwc, enum phy_mode mode) -{ - lockdep_assert_held(&appledwc->lock); - - /* - * This platform requires SUSPHY to be enabled here already in order to properly configure - * the PHY and switch dwc3's PIPE interface to USB3 PHY. - */ - dwc3_enable_susphy(&appledwc->dwc, true); - phy_set_mode(appledwc->dwc.usb2_generic_phy[0], mode); - phy_set_mode(appledwc->dwc.usb3_generic_phy[0], mode); -} - static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state) { int ret, ret_reset; lockdep_assert_held(&appledwc->lock); + /* + * The USB2 PHY on this platform must be configured for host or device mode while it is + * still powered off and before dwc3 tries to access it. Otherwise, the new configuration + * will sometimes only take affect after the *next* time dwc3 is brought up which causes + * the connected device to just not work. + * The USB3 PHY must be configured later after dwc3 has already been initialized. + */ + switch (state) { + case DWC3_APPLE_HOST: + phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_HOST); + break; + case DWC3_APPLE_DEVICE: + phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_DEVICE); + break; + default: + /* Unreachable unless there's a bug in this driver */ + return -EINVAL; + } + ret = reset_control_deassert(appledwc->reset); if (ret) { dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret); @@ -257,7 +263,13 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st case DWC3_APPLE_HOST: appledwc->dwc.dr_mode = USB_DR_MODE_HOST; dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST); - dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_HOST); + /* + * This platform requires SUSPHY to be enabled here already in order to properly + * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY + * has already been configured to the correct mode earlier. + */ + dwc3_enable_susphy(&appledwc->dwc, true); + phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_HOST); ret = dwc3_host_init(&appledwc->dwc); if (ret) { dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret); @@ -268,7 +280,13 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st case DWC3_APPLE_DEVICE: appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL; dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE); - dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_DEVICE); + /* + * This platform requires SUSPHY to be enabled here already in order to properly + * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY + * has already been configured to the correct mode earlier. + */ + dwc3_enable_susphy(&appledwc->dwc, true); + phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(&appledwc->dwc); if (ret) { dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret); @@ -340,6 +358,22 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role ro guard(mutex)(&appledwc->lock); /* + * Skip role switches if appledwc is already in the desired state. The + * USB-C port controller on M2 and M1/M2 Pro/Max/Ultra devices issues + * additional interrupts which results in usb_role_switch_set_role() + * calls with the current role. + * Ignore those calls here to ensure the USB-C port controller and + * appledwc are in a consistent state. + * This matches the behaviour in __dwc3_set_mode(). + * Do no handle USB_ROLE_NONE for DWC3_APPLE_NO_CABLE and + * DWC3_APPLE_PROBE_PENDING since that is no-op anyway. + */ + if (appledwc->state == DWC3_APPLE_HOST && role == USB_ROLE_HOST) + return 0; + if (appledwc->state == DWC3_APPLE_DEVICE && role == USB_ROLE_DEVICE) + return 0; + + /* * We need to tear all of dwc3 down and re-initialize it every time a cable is * connected or disconnected or when the mode changes. See the documentation for enum * dwc3_apple_state for details. diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index aa6ab666741a..a96476507d2f 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -362,6 +362,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) return ret; usb_ep_enable(uvc->video.ep); + uvc->video.max_req_size = uvc->video.ep->maxpacket + * max_t(unsigned int, uvc->video.ep->maxburst, 1) + * (uvc->video.ep->mult); + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; v4l2_event_queue(&uvc->vdev, &v4l2_event); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 9e79cbe50715..676419a04976 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -107,7 +107,7 @@ struct uvc_video { unsigned int width; unsigned int height; unsigned int imagesize; - unsigned int interval; + unsigned int interval; /* in 100ns units */ struct mutex mutex; /* protects frame parameters */ unsigned int uvc_num_requests; @@ -117,6 +117,7 @@ struct uvc_video { /* Requests */ bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; + unsigned int max_req_size; struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ /* USB requests that the video pump thread can encode into */ diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 9a1bbd79ff5a..586e5524c171 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -86,10 +86,17 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) buf->bytesused = 0; } else { buf->bytesused = vb2_get_plane_payload(vb, 0); - buf->req_payload_size = - DIV_ROUND_UP(buf->bytesused + - (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN), - video->reqs_per_frame); + + if (video->reqs_per_frame != 0) { + buf->req_payload_size = + DIV_ROUND_UP(buf->bytesused + + (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN), + video->reqs_per_frame); + if (buf->req_payload_size > video->req_size) + buf->req_payload_size = video->req_size; + } else { + buf->req_payload_size = video->max_req_size; + } } return 0; @@ -175,7 +182,15 @@ int uvcg_alloc_buffers(struct uvc_video_queue *queue, { int ret; +retry: ret = vb2_reqbufs(&queue->queue, rb); + if (ret < 0 && queue->use_sg) { + uvc_trace(UVC_TRACE_IOCTL, + "failed to alloc buffer with sg enabled, try non-sg mode\n"); + queue->use_sg = 0; + queue->queue.mem_ops = &vb2_vmalloc_memops; + goto retry; + } return ret ? ret : rb->count; } diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index fb77b0b21790..f568dee08b3b 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -499,13 +499,11 @@ uvc_video_prep_requests(struct uvc_video *video) { struct uvc_device *uvc = container_of(video, struct uvc_device, video); struct usb_composite_dev *cdev = uvc->func.config->cdev; - unsigned int interval_duration = video->ep->desc->bInterval * 1250; + unsigned int interval_duration; unsigned int max_req_size, req_size, header_size; unsigned int nreq; - max_req_size = video->ep->maxpacket - * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult); + max_req_size = video->max_req_size; if (!usb_endpoint_xfer_isoc(video->ep->desc)) { video->req_size = max_req_size; @@ -515,8 +513,11 @@ uvc_video_prep_requests(struct uvc_video *video) return; } + interval_duration = 2 << (video->ep->desc->bInterval - 1); if (cdev->gadget->speed < USB_SPEED_HIGH) - interval_duration = video->ep->desc->bInterval * 10000; + interval_duration *= 10000; + else + interval_duration *= 1250; nreq = DIV_ROUND_UP(video->interval, interval_duration); @@ -837,7 +838,6 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) video->interval = 666666; /* Initialize the video buffers queue. */ - uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, + return uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex); - return 0; } diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 2e4bb5cc2165..c801527d5bd2 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -392,3 +392,4 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Hauke Mehrtens"); MODULE_AUTHOR("Alan Stern"); MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: ehci_platform"); diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 5e02f2ceafb6..f4419d4526c4 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -211,3 +211,4 @@ static struct platform_driver uhci_platform_driver = { .of_match_table = platform_uhci_ids, }, }; +MODULE_SOFTDEP("pre: ehci_platform"); diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index a85f62a73313..2bd77255032b 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -210,7 +210,6 @@ xhci_sideband_remove_endpoint(struct xhci_sideband *sb, return -ENODEV; __xhci_sideband_remove_endpoint(sb, ep); - xhci_initialize_ring_info(ep->ring); return 0; } diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 31ccced5125e..8b492871d21d 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1563,7 +1563,7 @@ static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xu for (i = 0; i < tegra->soc->max_num_wakes; i++) { struct irq_data *data; - tegra->wake_irqs[i] = platform_get_irq(pdev, i + WAKE_IRQ_START_INDEX); + tegra->wake_irqs[i] = platform_get_irq_optional(pdev, i + WAKE_IRQ_START_INDEX); if (tegra->wake_irqs[i] < 0) break; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 02c9bfe21ae2..b3ba16b9718c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2898,16 +2898,25 @@ int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int gfp_t gfp_flags) { struct xhci_command *command; + struct xhci_ep_ctx *ep_ctx; unsigned long flags; - int ret; + int ret = -ENODEV; command = xhci_alloc_command(xhci, true, gfp_flags); if (!command) return -ENOMEM; spin_lock_irqsave(&xhci->lock, flags); - ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id, - ep->ep_index, suspend); + + /* make sure endpoint exists and is running before stopping it */ + if (ep->ring) { + ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index); + if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) + ret = xhci_queue_stop_endpoint(xhci, command, + ep->vdev->slot_id, + ep->ep_index, suspend); + } + if (ret < 0) { spin_unlock_irqrestore(&xhci->lock, flags); goto out; diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 530b77fc2f78..9262a2ac97f5 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -70,7 +70,6 @@ MODULE_DEVICE_TABLE(usb, combined_id_table); #define F81232_REGISTER_REQUEST 0xa0 #define F81232_GET_REGISTER 0xc0 #define F81232_SET_REGISTER 0x40 -#define F81534A_ACCESS_REG_RETRY 2 #define SERIAL_BASE_ADDRESS 0x0120 #define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS) @@ -824,36 +823,31 @@ static void f81232_lsr_worker(struct work_struct *work) static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg, u16 size, void *val) { - struct usb_device *dev = interface_to_usbdev(intf); - int retry = F81534A_ACCESS_REG_RETRY; - int status; - - while (retry--) { - status = usb_control_msg_send(dev, - 0, - F81232_REGISTER_REQUEST, - F81232_SET_REGISTER, - reg, - 0, - val, - size, - USB_CTRL_SET_TIMEOUT, - GFP_KERNEL); - if (status) { - status = usb_translate_errors(status); - if (status == -EIO) - continue; - } - - break; - } - - if (status) { - dev_err(&intf->dev, "failed to set register 0x%x: %d\n", - reg, status); - } + return usb_control_msg_send(interface_to_usbdev(intf), + 0, + F81232_REGISTER_REQUEST, + F81232_SET_REGISTER, + reg, + 0, + val, + size, + USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); +} - return status; +static int f81534a_ctrl_get_register(struct usb_interface *intf, u16 reg, + u16 size, void *val) +{ + return usb_control_msg_recv(interface_to_usbdev(intf), + 0, + F81232_REGISTER_REQUEST, + F81232_GET_REGISTER, + reg, + 0, + val, + size, + USB_CTRL_GET_TIMEOUT, + GFP_KERNEL); } static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en) @@ -869,6 +863,29 @@ static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en) * bit 0~11 : Serial port enable bit. */ if (en) { + /* + * The Fintek F81532A/534A/535/536 family relies on the + * F81534A_CTRL_CMD_ENABLE_PORT (116h) register during + * initialization to both determine serial port status and + * control port creation. + * + * If the driver experiences fast load/unload cycles, the + * device state may becomes unstable, resulting in the + * incomplete generation of serial ports. + * + * Performing a dummy read operation on the register prior + * to the initial write command resolves the issue. + * + * This clears the device's stale internal state. Subsequent + * write operations will correctly generate all serial ports. + */ + status = f81534a_ctrl_get_register(intf, + F81534A_CTRL_CMD_ENABLE_PORT, + sizeof(enable), + enable); + if (status) + return status; + enable[0] = 0xff; enable[1] = 0x8f; } diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index fe2f21d85737..acb48b1c83f7 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -848,6 +848,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID, 1) }, + { USB_DEVICE(FTDI_VID, FTDI_AXE027_PID) }, { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TURTELIZER_PID, 1) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_SCU18) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 2539b9e2f712..6c76cfebfd0e 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -96,6 +96,8 @@ #define LMI_LM3S_EVAL_BOARD_PID 0xbcd9 #define LMI_LM3S_ICDI_BOARD_PID 0xbcda +#define FTDI_AXE027_PID 0xBD90 /* PICAXE AXE027 USB download cable */ + #define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */ /* OpenDCC (www.opendcc.de) product id */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 4c0e5a3ab557..9f2cc5fb9f45 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1505,6 +1505,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1231, 0xff), /* Telit LE910Cx (RNDIS) */ .driver_info = NCTRL(2) | RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x1250, 0xff, 0x00, 0x00) }, /* Telit LE910Cx (rmnet) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1252, 0xff) }, /* Telit LE910Cx (MBIM) */ { USB_DEVICE(TELIT_VENDOR_ID, 0x1260), .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, { USB_DEVICE(TELIT_VENDOR_ID, 0x1261), diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 4ca2746ce16b..be49a976428f 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -7890,7 +7890,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->partner_desc.identity = &port->partner_ident; port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode); - if (!port->role_sw) + if (IS_ERR_OR_NULL(port->role_sw)) port->role_sw = usb_role_switch_get(port->dev); if (IS_ERR(port->role_sw)) { err = PTR_ERR(port->role_sw); diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index 59409c1fc3de..2f7bd2fdc616 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -75,4 +75,7 @@ /* short SET_ADDRESS request timeout */ #define USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT BIT(16) +/* skip BOS descriptor request */ +#define USB_QUIRK_NO_BOS BIT(17) + #endif /* __LINUX_USB_QUIRKS_H */ |
