mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
USB / Thunderbolt fixes for 6.10-rc4
Here are some small USB and Thunderbolt driver fixes for 6.10-rc4. Included in here are: - thunderbolt debugfs bugfix - USB typec bugfixes - kcov usb bugfix - xhci bugfixes - usb-storage bugfix - dt-bindings bugfix - cdc-wdm log message spam bugfix All of these, except for the last cdc-wdm log level change, have been in linux-next for a while with no reported problems. The cdc-wdm bugfix has been tested by syzbot and proved to fix the reported cpu lockup issues when the log is constantly spammed by a broken device. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZm68wA8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylZmgCfeRfuQkFXgRs1LYZ8x2g2FfHmxFgAoJe+2RAW yR6Lp2bPhH+YQS3rhjNL =hNwg -----END PGP SIGNATURE----- Merge tag 'usb-6.10-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB / Thunderbolt fixes from Greg KH: "Here are some small USB and Thunderbolt driver fixes for 6.10-rc4. Included in here are: - thunderbolt debugfs bugfix - USB typec bugfixes - kcov usb bugfix - xhci bugfixes - usb-storage bugfix - dt-bindings bugfix - cdc-wdm log message spam bugfix All of these, except for the last cdc-wdm log level change, have been in linux-next for a while with no reported problems. The cdc-wdm bugfix has been tested by syzbot and proved to fix the reported cpu lockup issues when the log is constantly spammed by a broken device" * tag 'usb-6.10-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: USB: class: cdc-wdm: Fix CPU lockup caused by excessive log messages xhci: Handle TD clearing for multiple streams case xhci: Apply broken streams quirk to Etron EJ188 xHCI host xhci: Apply reset resume quirk to Etron EJ188 xHCI host xhci: Set correct transferred length for cancelled bulk transfers usb-storage: alauda: Check whether the media is initialized usb: typec: ucsi: Ack also failed Get Error commands kcov, usb: disable interrupts in kcov_remote_start_usb_softirq dt-bindings: usb: realtek,rts5411: Add missing "additionalProperties" on child nodes usb: typec: tcpm: Ignore received Hard Reset in TOGGLING state usb: typec: tcpm: fix use-after-free case in tcpm_register_source_caps USB: xen-hcd: Traverse host/ when CONFIG_USB_XEN_HCD is selected usb: typec: ucsi: glink: increase max ports for x1e80100 Revert "usb: chipidea: move ci_ulpi_init after the phy initialization" thunderbolt: debugfs: Fix margin debugfs node creation condition
This commit is contained in:
commit
b5beaa4474
@ -65,6 +65,7 @@ patternProperties:
|
||||
description: The hard wired USB devices
|
||||
type: object
|
||||
$ref: /schemas/usb/usb-device.yaml
|
||||
additionalProperties: true
|
||||
|
||||
required:
|
||||
- peer-hub
|
||||
|
@ -943,8 +943,9 @@ static void margining_port_init(struct tb_port *port)
|
||||
debugfs_create_file("run", 0600, dir, port, &margining_run_fops);
|
||||
debugfs_create_file("results", 0600, dir, port, &margining_results_fops);
|
||||
debugfs_create_file("test", 0600, dir, port, &margining_test_fops);
|
||||
if (independent_voltage_margins(usb4) ||
|
||||
(supports_time(usb4) && independent_time_margins(usb4)))
|
||||
if (independent_voltage_margins(usb4) == USB4_MARGIN_CAP_0_VOLTAGE_HL ||
|
||||
(supports_time(usb4) &&
|
||||
independent_time_margins(usb4) == USB4_MARGIN_CAP_1_TIME_LR))
|
||||
debugfs_create_file("margin", 0600, dir, port, &margining_margin_fops);
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += host/
|
||||
obj-$(CONFIG_USB_FSL_USB2) += host/
|
||||
obj-$(CONFIG_USB_FOTG210_HCD) += host/
|
||||
obj-$(CONFIG_USB_MAX3421_HCD) += host/
|
||||
obj-$(CONFIG_USB_XEN_HCD) += host/
|
||||
|
||||
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
|
||||
|
||||
|
@ -1084,6 +1084,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = ci_ulpi_init(ci);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ci->platdata->phy) {
|
||||
ci->phy = ci->platdata->phy;
|
||||
} else if (ci->platdata->usb_phy) {
|
||||
@ -1138,10 +1142,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
goto ulpi_exit;
|
||||
}
|
||||
|
||||
ret = ci_ulpi_init(ci);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ci->hw_bank.phys = res->start;
|
||||
|
||||
ci->irq = platform_get_irq(pdev, 0);
|
||||
|
@ -68,6 +68,11 @@ int ci_ulpi_init(struct ci_hdrc *ci)
|
||||
if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Set PORTSC correctly so we can read/write ULPI registers for
|
||||
* identification purposes
|
||||
*/
|
||||
hw_phymode_configure(ci);
|
||||
|
||||
ci->ulpi_ops.read = ci_ulpi_read;
|
||||
ci->ulpi_ops.write = ci_ulpi_write;
|
||||
|
@ -266,14 +266,14 @@ static void wdm_int_callback(struct urb *urb)
|
||||
dev_err(&desc->intf->dev, "Stall on int endpoint\n");
|
||||
goto sw; /* halt is cleared in work */
|
||||
default:
|
||||
dev_err(&desc->intf->dev,
|
||||
dev_err_ratelimited(&desc->intf->dev,
|
||||
"nonzero urb status received: %d\n", status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
|
||||
dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
|
||||
dev_err_ratelimited(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
|
||||
urb->actual_length);
|
||||
goto exit;
|
||||
}
|
||||
|
@ -1623,6 +1623,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
|
||||
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
|
||||
struct usb_anchor *anchor = urb->anchor;
|
||||
int status = urb->unlinked;
|
||||
unsigned long flags;
|
||||
|
||||
urb->hcpriv = NULL;
|
||||
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
|
||||
@ -1640,13 +1641,14 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
|
||||
/* pass ownership to the completion handler */
|
||||
urb->status = status;
|
||||
/*
|
||||
* This function can be called in task context inside another remote
|
||||
* coverage collection section, but kcov doesn't support that kind of
|
||||
* recursion yet. Only collect coverage in softirq context for now.
|
||||
* Only collect coverage in the softirq context and disable interrupts
|
||||
* to avoid scenarios with nested remote coverage collection sections
|
||||
* that KCOV does not support.
|
||||
* See the comment next to kcov_remote_start_usb_softirq() for details.
|
||||
*/
|
||||
kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
|
||||
flags = kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
|
||||
urb->complete(urb);
|
||||
kcov_remote_stop_softirq();
|
||||
kcov_remote_stop_softirq(flags);
|
||||
|
||||
usb_anchor_resume_wakeups(anchor);
|
||||
atomic_dec(&urb->use_count);
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#define PCI_VENDOR_ID_ETRON 0x1b6f
|
||||
#define PCI_DEVICE_ID_EJ168 0x7023
|
||||
#define PCI_DEVICE_ID_EJ188 0x7052
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
|
||||
@ -395,6 +396,12 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
xhci->quirks |= XHCI_BROKEN_STREAMS;
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
|
||||
pdev->device == PCI_DEVICE_ID_EJ188) {
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
xhci->quirks |= XHCI_BROKEN_STREAMS;
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
|
||||
pdev->device == 0x0014) {
|
||||
xhci->quirks |= XHCI_ZERO_64B_REGS;
|
||||
|
@ -1031,13 +1031,27 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||||
break;
|
||||
case TD_DIRTY: /* TD is cached, clear it */
|
||||
case TD_HALTED:
|
||||
case TD_CLEARING_CACHE_DEFERRED:
|
||||
if (cached_td) {
|
||||
if (cached_td->urb->stream_id != td->urb->stream_id) {
|
||||
/* Multiple streams case, defer move dq */
|
||||
xhci_dbg(xhci,
|
||||
"Move dq deferred: stream %u URB %p\n",
|
||||
td->urb->stream_id, td->urb);
|
||||
td->cancel_status = TD_CLEARING_CACHE_DEFERRED;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Should never happen, but clear the TD if it does */
|
||||
xhci_warn(xhci,
|
||||
"Found multiple active URBs %p and %p in stream %u?\n",
|
||||
td->urb, cached_td->urb,
|
||||
td->urb->stream_id);
|
||||
td_to_noop(xhci, ring, cached_td, false);
|
||||
cached_td->cancel_status = TD_CLEARED;
|
||||
}
|
||||
|
||||
td->cancel_status = TD_CLEARING_CACHE;
|
||||
if (cached_td)
|
||||
/* FIXME stream case, several stopped rings */
|
||||
xhci_dbg(xhci,
|
||||
"Move dq past stream %u URB %p instead of stream %u URB %p\n",
|
||||
td->urb->stream_id, td->urb,
|
||||
cached_td->urb->stream_id, cached_td->urb);
|
||||
cached_td = td;
|
||||
break;
|
||||
}
|
||||
@ -1057,10 +1071,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||||
if (err) {
|
||||
/* Failed to move past cached td, just set cached TDs to no-op */
|
||||
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
|
||||
if (td->cancel_status != TD_CLEARING_CACHE)
|
||||
/*
|
||||
* Deferred TDs need to have the deq pointer set after the above command
|
||||
* completes, so if that failed we just give up on all of them (and
|
||||
* complain loudly since this could cause issues due to caching).
|
||||
*/
|
||||
if (td->cancel_status != TD_CLEARING_CACHE &&
|
||||
td->cancel_status != TD_CLEARING_CACHE_DEFERRED)
|
||||
continue;
|
||||
xhci_dbg(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
|
||||
td->urb);
|
||||
xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
|
||||
td->urb);
|
||||
td_to_noop(xhci, ring, td, false);
|
||||
td->cancel_status = TD_CLEARED;
|
||||
}
|
||||
@ -1346,6 +1366,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
struct xhci_slot_ctx *slot_ctx;
|
||||
struct xhci_td *td, *tmp_td;
|
||||
bool deferred = false;
|
||||
|
||||
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
|
||||
stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
|
||||
@ -1432,6 +1453,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
||||
xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n",
|
||||
__func__, td->urb);
|
||||
xhci_td_cleanup(ep->xhci, td, ep_ring, td->status);
|
||||
} else if (td->cancel_status == TD_CLEARING_CACHE_DEFERRED) {
|
||||
deferred = true;
|
||||
} else {
|
||||
xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n",
|
||||
__func__, td->urb, td->cancel_status);
|
||||
@ -1441,8 +1464,17 @@ cleanup:
|
||||
ep->ep_state &= ~SET_DEQ_PENDING;
|
||||
ep->queued_deq_seg = NULL;
|
||||
ep->queued_deq_ptr = NULL;
|
||||
/* Restart any rings with pending URBs */
|
||||
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||||
|
||||
if (deferred) {
|
||||
/* We have more streams to clear */
|
||||
xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n",
|
||||
__func__);
|
||||
xhci_invalidate_cancelled_tds(ep);
|
||||
} else {
|
||||
/* Restart any rings with pending URBs */
|
||||
xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__);
|
||||
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||||
}
|
||||
}
|
||||
|
||||
static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
|
||||
@ -2524,9 +2556,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
goto finish_td;
|
||||
case COMP_STOPPED_LENGTH_INVALID:
|
||||
/* stopped on ep trb with invalid length, exclude it */
|
||||
ep_trb_len = 0;
|
||||
remaining = 0;
|
||||
break;
|
||||
td->urb->actual_length = sum_trb_lengths(xhci, ep_ring, ep_trb);
|
||||
goto finish_td;
|
||||
case COMP_USB_TRANSACTION_ERROR:
|
||||
if (xhci->quirks & XHCI_NO_SOFT_RETRY ||
|
||||
(ep->err_count++ > MAX_SOFT_RETRY) ||
|
||||
|
@ -1276,6 +1276,7 @@ enum xhci_cancelled_td_status {
|
||||
TD_DIRTY = 0,
|
||||
TD_HALTED,
|
||||
TD_CLEARING_CACHE,
|
||||
TD_CLEARING_CACHE_DEFERRED,
|
||||
TD_CLEARED,
|
||||
};
|
||||
|
||||
|
@ -105,6 +105,8 @@ struct alauda_info {
|
||||
unsigned char sense_key;
|
||||
unsigned long sense_asc; /* additional sense code */
|
||||
unsigned long sense_ascq; /* additional sense code qualifier */
|
||||
|
||||
bool media_initialized;
|
||||
};
|
||||
|
||||
#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
|
||||
@ -476,11 +478,12 @@ static int alauda_check_media(struct us_data *us)
|
||||
}
|
||||
|
||||
/* Check for media change */
|
||||
if (status[0] & 0x08) {
|
||||
if (status[0] & 0x08 || !info->media_initialized) {
|
||||
usb_stor_dbg(us, "Media change detected\n");
|
||||
alauda_free_maps(&MEDIA_INFO(us));
|
||||
alauda_init_media(us);
|
||||
|
||||
rc = alauda_init_media(us);
|
||||
if (rc == USB_STOR_TRANSPORT_GOOD)
|
||||
info->media_initialized = true;
|
||||
info->sense_key = UNIT_ATTENTION;
|
||||
info->sense_asc = 0x28;
|
||||
info->sense_ascq = 0x00;
|
||||
|
@ -3014,8 +3014,10 @@ static int tcpm_register_source_caps(struct tcpm_port *port)
|
||||
memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps);
|
||||
caps.role = TYPEC_SOURCE;
|
||||
|
||||
if (cap)
|
||||
if (cap) {
|
||||
usb_power_delivery_unregister_capabilities(cap);
|
||||
port->partner_source_caps = NULL;
|
||||
}
|
||||
|
||||
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
|
||||
if (IS_ERR(cap))
|
||||
@ -6172,6 +6174,7 @@ static void _tcpm_pd_hard_reset(struct tcpm_port *port)
|
||||
port->tcpc->set_bist_data(port->tcpc, false);
|
||||
|
||||
switch (port->state) {
|
||||
case TOGGLING:
|
||||
case ERROR_RECOVERY:
|
||||
case PORT_RESET:
|
||||
case PORT_RESET_WAIT_OFF:
|
||||
|
@ -153,8 +153,13 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
|
||||
}
|
||||
|
||||
if (cci & UCSI_CCI_ERROR) {
|
||||
if (cmd == UCSI_GET_ERROR_STATUS)
|
||||
if (cmd == UCSI_GET_ERROR_STATUS) {
|
||||
ret = ucsi_acknowledge(ucsi, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
return ucsi_read_error(ucsi);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <linux/soc/qcom/pmic_glink.h>
|
||||
#include "ucsi.h"
|
||||
|
||||
#define PMIC_GLINK_MAX_PORTS 2
|
||||
#define PMIC_GLINK_MAX_PORTS 3
|
||||
|
||||
#define UCSI_BUF_SIZE 48
|
||||
|
||||
|
@ -55,21 +55,47 @@ static inline void kcov_remote_start_usb(u64 id)
|
||||
|
||||
/*
|
||||
* The softirq flavor of kcov_remote_*() functions is introduced as a temporary
|
||||
* work around for kcov's lack of nested remote coverage sections support in
|
||||
* task context. Adding support for nested sections is tracked in:
|
||||
* https://bugzilla.kernel.org/show_bug.cgi?id=210337
|
||||
* workaround for KCOV's lack of nested remote coverage sections support.
|
||||
*
|
||||
* Adding support is tracked in https://bugzilla.kernel.org/show_bug.cgi?id=210337.
|
||||
*
|
||||
* kcov_remote_start_usb_softirq():
|
||||
*
|
||||
* 1. Only collects coverage when called in the softirq context. This allows
|
||||
* avoiding nested remote coverage collection sections in the task context.
|
||||
* For example, USB/IP calls usb_hcd_giveback_urb() in the task context
|
||||
* within an existing remote coverage collection section. Thus, KCOV should
|
||||
* not attempt to start collecting coverage within the coverage collection
|
||||
* section in __usb_hcd_giveback_urb() in this case.
|
||||
*
|
||||
* 2. Disables interrupts for the duration of the coverage collection section.
|
||||
* This allows avoiding nested remote coverage collection sections in the
|
||||
* softirq context (a softirq might occur during the execution of a work in
|
||||
* the BH workqueue, which runs with in_serving_softirq() > 0).
|
||||
* For example, usb_giveback_urb_bh() runs in the BH workqueue with
|
||||
* interrupts enabled, so __usb_hcd_giveback_urb() might be interrupted in
|
||||
* the middle of its remote coverage collection section, and the interrupt
|
||||
* handler might invoke __usb_hcd_giveback_urb() again.
|
||||
*/
|
||||
|
||||
static inline void kcov_remote_start_usb_softirq(u64 id)
|
||||
static inline unsigned long kcov_remote_start_usb_softirq(u64 id)
|
||||
{
|
||||
if (in_serving_softirq())
|
||||
unsigned long flags = 0;
|
||||
|
||||
if (in_serving_softirq()) {
|
||||
local_irq_save(flags);
|
||||
kcov_remote_start_usb(id);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static inline void kcov_remote_stop_softirq(void)
|
||||
static inline void kcov_remote_stop_softirq(unsigned long flags)
|
||||
{
|
||||
if (in_serving_softirq())
|
||||
if (in_serving_softirq()) {
|
||||
kcov_remote_stop();
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
@ -103,8 +129,11 @@ static inline u64 kcov_common_handle(void)
|
||||
}
|
||||
static inline void kcov_remote_start_common(u64 id) {}
|
||||
static inline void kcov_remote_start_usb(u64 id) {}
|
||||
static inline void kcov_remote_start_usb_softirq(u64 id) {}
|
||||
static inline void kcov_remote_stop_softirq(void) {}
|
||||
static inline unsigned long kcov_remote_start_usb_softirq(u64 id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void kcov_remote_stop_softirq(unsigned long flags) {}
|
||||
|
||||
#endif /* CONFIG_KCOV */
|
||||
#endif /* _LINUX_KCOV_H */
|
||||
|
Loading…
Reference in New Issue
Block a user