mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 04:38:03 +00:00
USB driver fixes for 6.8-rc3
Here are a bunch of small USB driver fixes for 6.8-rc3. Included in here are: - new usb-serial driver ids - new dwc3 driver id added - typec driver change revert - ncm gadget driver endian bugfix - xhci bugfixes for a number of reported issues - usb hub bugfix for alternate settings - ulpi driver debugfs memory leak fix - chipidea driver bugfix - usb gadget driver fixes All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZb60Zg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymczwCcCwsgyz86WT5ncgcMTcCFJ0RHEFUAoMLTb7PO Ilvy8z+Wn2I2QEtnDLqT =H8kH -----END PGP SIGNATURE----- Merge tag 'usb-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB driver fixes from Greg KH: "Here are a bunch of small USB driver fixes for 6.8-rc3. Included in here are: - new usb-serial driver ids - new dwc3 driver id added - typec driver change revert - ncm gadget driver endian bugfix - xhci bugfixes for a number of reported issues - usb hub bugfix for alternate settings - ulpi driver debugfs memory leak fix - chipidea driver bugfix - usb gadget driver fixes All of these have been in linux-next for a while with no reported issues" * tag 'usb-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (24 commits) USB: serial: option: add Fibocom FM101-GL variant USB: serial: qcserial: add new usb-id for Dell Wireless DW5826e USB: serial: cp210x: add ID for IMST iM871A-USB usb: typec: tcpm: fix the PD disabled case usb: ucsi_acpi: Quirk to ack a connector change ack cmd usb: ucsi_acpi: Fix command completion handling usb: ucsi: Add missing ppm_lock usb: ulpi: Fix debugfs directory leak Revert "usb: typec: tcpm: fix cc role at port reset" usb: gadget: pch_udc: fix an Excess kernel-doc warning usb: f_mass_storage: forbid async queue when shutdown happen USB: hub: check for alternate port before enabling A_ALT_HNP_SUPPORT usb: chipidea: core: handle power lost in workqueue usb: dwc3: gadget: Fix NULL pointer dereference in dwc3_gadget_suspend usb: dwc3: pci: add support for the Intel Arrow Lake-H usb: core: Prevent null pointer dereference in update_port_device_state xhci: handle isoc Babble and Buffer Overrun events properly xhci: process isoc TD properly when there was a transaction error mid TD. xhci: fix off by one check when adding a secondary interrupter. xhci: fix possible null pointer dereference at secondary interrupter removal ...
This commit is contained in:
commit
809be620dc
@ -448,17 +448,17 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "ncm".
|
||||
The NCM function provides these attributes in its function directory:
|
||||
|
||||
=============== ==================================================
|
||||
ifname network device interface name associated with this
|
||||
function instance
|
||||
qmult queue length multiplier for high and super speed
|
||||
host_addr MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr MAC address of device's end of this
|
||||
Ethernet over USB link
|
||||
max_segment_size Segment size required for P2P connections. This
|
||||
will set MTU to (max_segment_size - 14 bytes)
|
||||
=============== ==================================================
|
||||
======================= ==================================================
|
||||
ifname network device interface name associated with this
|
||||
function instance
|
||||
qmult queue length multiplier for high and super speed
|
||||
host_addr MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr MAC address of device's end of this
|
||||
Ethernet over USB link
|
||||
max_segment_size Segment size required for P2P connections. This
|
||||
will set MTU to 14 bytes
|
||||
======================= ==================================================
|
||||
|
||||
and after creating the functions/ncm.<instance name> they contain default
|
||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
||||
|
@ -176,6 +176,7 @@ struct hw_bank {
|
||||
* @enabled_otg_timer_bits: bits of enabled otg timers
|
||||
* @next_otg_timer: next nearest enabled timer to be expired
|
||||
* @work: work for role changing
|
||||
* @power_lost_work: work for power lost handling
|
||||
* @wq: workqueue thread
|
||||
* @qh_pool: allocation pool for queue heads
|
||||
* @td_pool: allocation pool for transfer descriptors
|
||||
@ -226,6 +227,7 @@ struct ci_hdrc {
|
||||
enum otg_fsm_timer next_otg_timer;
|
||||
struct usb_role_switch *role_switch;
|
||||
struct work_struct work;
|
||||
struct work_struct power_lost_work;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
struct dma_pool *qh_pool;
|
||||
|
@ -856,6 +856,27 @@ static int ci_extcon_register(struct ci_hdrc *ci)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ci_power_lost_work(struct work_struct *work)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(work, struct ci_hdrc, power_lost_work);
|
||||
enum ci_role role;
|
||||
|
||||
disable_irq_nosync(ci->irq);
|
||||
pm_runtime_get_sync(ci->dev);
|
||||
if (!ci_otg_is_fsm_mode(ci)) {
|
||||
role = ci_get_role(ci);
|
||||
|
||||
if (ci->role != role) {
|
||||
ci_handle_id_switch(ci);
|
||||
} else if (role == CI_ROLE_GADGET) {
|
||||
if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV))
|
||||
usb_gadget_vbus_connect(&ci->gadget);
|
||||
}
|
||||
}
|
||||
pm_runtime_put_sync(ci->dev);
|
||||
enable_irq(ci->irq);
|
||||
}
|
||||
|
||||
static DEFINE_IDA(ci_ida);
|
||||
|
||||
struct platform_device *ci_hdrc_add_device(struct device *dev,
|
||||
@ -1045,6 +1066,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
|
||||
spin_lock_init(&ci->lock);
|
||||
mutex_init(&ci->mutex);
|
||||
INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
|
||||
|
||||
ci->dev = dev;
|
||||
ci->platdata = dev_get_platdata(dev);
|
||||
ci->imx28_write_fix = !!(ci->platdata->flags &
|
||||
@ -1396,25 +1419,6 @@ static int ci_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ci_handle_power_lost(struct ci_hdrc *ci)
|
||||
{
|
||||
enum ci_role role;
|
||||
|
||||
disable_irq_nosync(ci->irq);
|
||||
if (!ci_otg_is_fsm_mode(ci)) {
|
||||
role = ci_get_role(ci);
|
||||
|
||||
if (ci->role != role) {
|
||||
ci_handle_id_switch(ci);
|
||||
} else if (role == CI_ROLE_GADGET) {
|
||||
if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV))
|
||||
usb_gadget_vbus_connect(&ci->gadget);
|
||||
}
|
||||
}
|
||||
|
||||
enable_irq(ci->irq);
|
||||
}
|
||||
|
||||
static int ci_resume(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
@ -1446,7 +1450,7 @@ static int ci_resume(struct device *dev)
|
||||
ci_role(ci)->resume(ci, power_lost);
|
||||
|
||||
if (power_lost)
|
||||
ci_handle_power_lost(ci);
|
||||
queue_work(system_freezable_wq, &ci->power_lost_work);
|
||||
|
||||
if (ci->supports_runtime_pm) {
|
||||
pm_runtime_disable(dev);
|
||||
|
@ -301,7 +301,7 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
root = debugfs_create_dir(dev_name(dev), ulpi_root);
|
||||
root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root);
|
||||
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
|
||||
|
||||
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
|
||||
|
@ -2053,9 +2053,19 @@ static void update_port_device_state(struct usb_device *udev)
|
||||
|
||||
if (udev->parent) {
|
||||
hub = usb_hub_to_struct_hub(udev->parent);
|
||||
port_dev = hub->ports[udev->portnum - 1];
|
||||
WRITE_ONCE(port_dev->state, udev->state);
|
||||
sysfs_notify_dirent(port_dev->state_kn);
|
||||
|
||||
/*
|
||||
* The Link Layer Validation System Driver (lvstest)
|
||||
* has a test step to unbind the hub before running the
|
||||
* rest of the procedure. This triggers hub_disconnect
|
||||
* which will set the hub's maxchild to 0, further
|
||||
* resulting in usb_hub_to_struct_hub returning NULL.
|
||||
*/
|
||||
if (hub) {
|
||||
port_dev = hub->ports[udev->portnum - 1];
|
||||
WRITE_ONCE(port_dev->state, udev->state);
|
||||
sysfs_notify_dirent(port_dev->state_kn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2388,17 +2398,25 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
|
||||
}
|
||||
} else if (desc->bLength == sizeof
|
||||
(struct usb_otg_descriptor)) {
|
||||
/* Set a_alt_hnp_support for legacy otg device */
|
||||
err = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_FEATURE, 0,
|
||||
USB_DEVICE_A_ALT_HNP_SUPPORT,
|
||||
0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (err < 0)
|
||||
dev_err(&udev->dev,
|
||||
"set a_alt_hnp_support failed: %d\n",
|
||||
err);
|
||||
/*
|
||||
* We are operating on a legacy OTP device
|
||||
* These should be told that they are operating
|
||||
* on the wrong port if we have another port that does
|
||||
* support HNP
|
||||
*/
|
||||
if (bus->otg_port != 0) {
|
||||
/* Set a_alt_hnp_support for legacy otg device */
|
||||
err = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_FEATURE, 0,
|
||||
USB_DEVICE_A_ALT_HNP_SUPPORT,
|
||||
0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (err < 0)
|
||||
dev_err(&udev->dev,
|
||||
"set a_alt_hnp_support failed: %d\n",
|
||||
err);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -51,6 +51,8 @@
|
||||
#define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1
|
||||
#define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f
|
||||
#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
|
||||
#define PCI_DEVICE_ID_INTEL_ARLH 0x7ec1
|
||||
#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e
|
||||
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15
|
||||
#define PCI_DEVICE_ID_AMD_MR 0x163a
|
||||
|
||||
@ -421,6 +423,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
||||
{ PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ARLH, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) },
|
||||
{ PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) },
|
||||
|
||||
{ PCI_DEVICE_DATA(AMD, NL_USB, &dwc3_pci_amd_swnode) },
|
||||
|
@ -4709,15 +4709,13 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!dwc->gadget_driver)
|
||||
return 0;
|
||||
|
||||
ret = dwc3_gadget_soft_disconnect(dwc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_disconnect_gadget(dwc);
|
||||
if (dwc->gadget_driver)
|
||||
dwc3_disconnect_gadget(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
@ -61,7 +61,7 @@ static int dwc3_host_get_irq(struct dwc3 *dwc)
|
||||
|
||||
int dwc3_host_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct property_entry props[4];
|
||||
struct property_entry props[5];
|
||||
struct platform_device *xhci;
|
||||
int ret, irq;
|
||||
int prop_idx = 0;
|
||||
@ -89,6 +89,8 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
|
||||
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
|
||||
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-sg-trb-cache-size-quirk");
|
||||
|
||||
if (dwc->usb3_lpm_capable)
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");
|
||||
|
||||
|
@ -545,21 +545,37 @@ static int start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
|
||||
|
||||
static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!fsg_is_set(common))
|
||||
return false;
|
||||
bh->state = BUF_STATE_SENDING;
|
||||
if (start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq))
|
||||
rc = start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq);
|
||||
if (rc) {
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
if (rc == -ESHUTDOWN) {
|
||||
common->running = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!fsg_is_set(common))
|
||||
return false;
|
||||
bh->state = BUF_STATE_RECEIVING;
|
||||
if (start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq))
|
||||
rc = start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq);
|
||||
if (rc) {
|
||||
bh->state = BUF_STATE_FULL;
|
||||
if (rc == -ESHUTDOWN) {
|
||||
common->running = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -105,8 +105,8 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
|
||||
|
||||
/*
|
||||
* Although max mtu as dictated by u_ether is 15412 bytes, setting
|
||||
* max_segment_sizeto 15426 would not be efficient. If user chooses segment
|
||||
* size to be (>= 8192), then we can't aggregate more than one buffer in each
|
||||
* max_segment_size to 15426 would not be efficient. If user chooses segment
|
||||
* size to be (>= 8192), then we can't aggregate more than one buffer in each
|
||||
* NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep
|
||||
* maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow
|
||||
* at least 2 packets to be aggregated reducing wastage of NTB buffer space
|
||||
@ -1489,7 +1489,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
ncm_data_intf.bInterfaceNumber = status;
|
||||
ncm_union_desc.bSlaveInterface0 = status;
|
||||
|
||||
ecm_desc.wMaxSegmentSize = ncm_opts->max_segment_size;
|
||||
ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size);
|
||||
|
||||
status = -ENODEV;
|
||||
|
||||
@ -1685,7 +1685,7 @@ static struct usb_function_instance *ncm_alloc_inst(void)
|
||||
kfree(opts);
|
||||
return ERR_CAST(net);
|
||||
}
|
||||
opts->max_segment_size = cpu_to_le16(ETH_FRAME_LEN);
|
||||
opts->max_segment_size = ETH_FRAME_LEN;
|
||||
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
|
||||
|
||||
descs[0] = &opts->ncm_os_desc;
|
||||
|
@ -274,7 +274,6 @@ struct pch_udc_cfg_data {
|
||||
* @td_data: for data request
|
||||
* @dev: reference to device struct
|
||||
* @offset_addr: offset address of ep register
|
||||
* @desc: for this ep
|
||||
* @queue: queue for requests
|
||||
* @num: endpoint number
|
||||
* @in: endpoint is IN
|
||||
|
@ -1861,15 +1861,15 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
unsigned int intr_num;
|
||||
|
||||
/* interrupter 0 is primary interrupter, don't touch it */
|
||||
if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters)
|
||||
xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
|
||||
|
||||
/* fixme, should we check xhci->interrupter[intr_num] == ir */
|
||||
/* fixme locking */
|
||||
|
||||
spin_lock_irq(&xhci->lock);
|
||||
|
||||
/* interrupter 0 is primary interrupter, don't touch it */
|
||||
if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters) {
|
||||
xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
intr_num = ir->intr_num;
|
||||
|
||||
xhci_remove_interrupter(xhci, ir);
|
||||
@ -2322,7 +2322,7 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
|
||||
u64 erst_base;
|
||||
u32 erst_size;
|
||||
|
||||
if (intr_num > xhci->max_interrupters) {
|
||||
if (intr_num >= xhci->max_interrupters) {
|
||||
xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n",
|
||||
intr_num, xhci->max_interrupters);
|
||||
return -EINVAL;
|
||||
|
@ -253,6 +253,9 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
|
||||
if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
|
||||
xhci->quirks |= XHCI_BROKEN_PORT_PED;
|
||||
|
||||
if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
|
||||
xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
|
||||
|
||||
device_property_read_u32(tmpdev, "imod-interval-ns",
|
||||
&xhci->imod_interval);
|
||||
}
|
||||
|
@ -2376,6 +2376,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
/* handle completion code */
|
||||
switch (trb_comp_code) {
|
||||
case COMP_SUCCESS:
|
||||
/* Don't overwrite status if TD had an error, see xHCI 4.9.1 */
|
||||
if (td->error_mid_td)
|
||||
break;
|
||||
if (remaining) {
|
||||
frame->status = short_framestatus;
|
||||
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
|
||||
@ -2391,9 +2394,13 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
case COMP_BANDWIDTH_OVERRUN_ERROR:
|
||||
frame->status = -ECOMM;
|
||||
break;
|
||||
case COMP_ISOCH_BUFFER_OVERRUN:
|
||||
case COMP_BABBLE_DETECTED_ERROR:
|
||||
sum_trbs_for_length = true;
|
||||
fallthrough;
|
||||
case COMP_ISOCH_BUFFER_OVERRUN:
|
||||
frame->status = -EOVERFLOW;
|
||||
if (ep_trb != td->last_trb)
|
||||
td->error_mid_td = true;
|
||||
break;
|
||||
case COMP_INCOMPATIBLE_DEVICE_ERROR:
|
||||
case COMP_STALL_ERROR:
|
||||
@ -2401,8 +2408,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
break;
|
||||
case COMP_USB_TRANSACTION_ERROR:
|
||||
frame->status = -EPROTO;
|
||||
sum_trbs_for_length = true;
|
||||
if (ep_trb != td->last_trb)
|
||||
return 0;
|
||||
td->error_mid_td = true;
|
||||
break;
|
||||
case COMP_STOPPED:
|
||||
sum_trbs_for_length = true;
|
||||
@ -2422,6 +2430,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
break;
|
||||
}
|
||||
|
||||
if (td->urb_length_set)
|
||||
goto finish_td;
|
||||
|
||||
if (sum_trbs_for_length)
|
||||
frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) +
|
||||
ep_trb_len - remaining;
|
||||
@ -2430,6 +2441,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
|
||||
td->urb->actual_length += frame->actual_length;
|
||||
|
||||
finish_td:
|
||||
/* Don't give back TD yet if we encountered an error mid TD */
|
||||
if (td->error_mid_td && ep_trb != td->last_trb) {
|
||||
xhci_dbg(xhci, "Error mid isoc TD, wait for final completion event\n");
|
||||
td->urb_length_set = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
|
||||
}
|
||||
|
||||
@ -2808,17 +2827,51 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
}
|
||||
|
||||
if (!ep_seg) {
|
||||
if (!ep->skip ||
|
||||
!usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
|
||||
/* Some host controllers give a spurious
|
||||
* successful event after a short transfer.
|
||||
* Ignore it.
|
||||
*/
|
||||
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
|
||||
ep_ring->last_td_was_short) {
|
||||
ep_ring->last_td_was_short = false;
|
||||
goto cleanup;
|
||||
|
||||
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
|
||||
skip_isoc_td(xhci, td, ep, status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some hosts give a spurious success event after a short
|
||||
* transfer. Ignore it.
|
||||
*/
|
||||
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
|
||||
ep_ring->last_td_was_short) {
|
||||
ep_ring->last_td_was_short = false;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* xhci 4.10.2 states isoc endpoints should continue
|
||||
* processing the next TD if there was an error mid TD.
|
||||
* So host like NEC don't generate an event for the last
|
||||
* isoc TRB even if the IOC flag is set.
|
||||
* xhci 4.9.1 states that if there are errors in mult-TRB
|
||||
* TDs xHC should generate an error for that TRB, and if xHC
|
||||
* proceeds to the next TD it should genete an event for
|
||||
* any TRB with IOC flag on the way. Other host follow this.
|
||||
* So this event might be for the next TD.
|
||||
*/
|
||||
if (td->error_mid_td &&
|
||||
!list_is_last(&td->td_list, &ep_ring->td_list)) {
|
||||
struct xhci_td *td_next = list_next_entry(td, td_list);
|
||||
|
||||
ep_seg = trb_in_td(xhci, td_next->start_seg, td_next->first_trb,
|
||||
td_next->last_trb, ep_trb_dma, false);
|
||||
if (ep_seg) {
|
||||
/* give back previous TD, start handling new */
|
||||
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
|
||||
ep_ring->dequeue = td->last_trb;
|
||||
ep_ring->deq_seg = td->last_trb_seg;
|
||||
inc_deq(xhci, ep_ring);
|
||||
xhci_td_cleanup(xhci, td, ep_ring, td->status);
|
||||
td = td_next;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ep_seg) {
|
||||
/* HC is busted, give up! */
|
||||
xhci_err(xhci,
|
||||
"ERROR Transfer event TRB DMA ptr not "
|
||||
@ -2830,9 +2883,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
ep_trb_dma, true);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
skip_isoc_td(xhci, td, ep, status);
|
||||
goto cleanup;
|
||||
}
|
||||
if (trb_comp_code == COMP_SHORT_PACKET)
|
||||
ep_ring->last_td_was_short = true;
|
||||
|
@ -1549,6 +1549,7 @@ struct xhci_td {
|
||||
struct xhci_segment *bounce_seg;
|
||||
/* actual_length of the URB has already been set */
|
||||
bool urb_length_set;
|
||||
bool error_mid_td;
|
||||
unsigned int num_trbs;
|
||||
};
|
||||
|
||||
|
@ -146,6 +146,7 @@ static const struct usb_device_id id_table[] = {
|
||||
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
|
||||
{ USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
|
||||
{ USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
|
||||
{ USB_DEVICE(0x10C4, 0x87ED) }, /* IMST USB-Stick for Smart Meter */
|
||||
{ USB_DEVICE(0x10C4, 0x8856) }, /* CEL EM357 ZigBee USB Stick - LR */
|
||||
{ USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */
|
||||
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
|
||||
|
@ -2269,6 +2269,7 @@ static const struct usb_device_id option_ids[] = {
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0111, 0xff) }, /* Fibocom FM160 (MBIM mode) */
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) }, /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a2, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a3, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a4, 0xff), /* Fibocom FM101-GL (laptop MBIM) */
|
||||
.driver_info = RSVD(4) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */
|
||||
|
@ -184,6 +184,8 @@ static const struct usb_device_id id_table[] = {
|
||||
{DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */
|
||||
{DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */
|
||||
{DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */
|
||||
{DEVICE_SWI(0x413c, 0x8217)}, /* Dell Wireless DW5826e */
|
||||
{DEVICE_SWI(0x413c, 0x8218)}, /* Dell Wireless DW5826e QDL */
|
||||
|
||||
/* Huawei devices */
|
||||
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
|
||||
|
@ -4876,8 +4876,7 @@ static void run_state_machine(struct tcpm_port *port)
|
||||
break;
|
||||
case PORT_RESET:
|
||||
tcpm_reset_port(port);
|
||||
tcpm_set_cc(port, tcpm_default_state(port) == SNK_UNATTACHED ?
|
||||
TYPEC_CC_RD : tcpm_rp_cc(port));
|
||||
tcpm_set_cc(port, TYPEC_CC_OPEN);
|
||||
tcpm_set_state(port, PORT_RESET_WAIT_OFF,
|
||||
PD_T_ERROR_RECOVERY);
|
||||
break;
|
||||
@ -6848,7 +6847,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
|
||||
if (err)
|
||||
goto out_role_sw_put;
|
||||
|
||||
port->typec_caps.pd = port->pds[0];
|
||||
if (port->pds)
|
||||
port->typec_caps.pd = port->pds[0];
|
||||
|
||||
port->typec_port = typec_register_port(port->dev, &port->typec_caps);
|
||||
if (IS_ERR(port->typec_port)) {
|
||||
|
@ -938,7 +938,9 @@ static void ucsi_handle_connector_change(struct work_struct *work)
|
||||
|
||||
clear_bit(EVENT_PENDING, &con->ucsi->flags);
|
||||
|
||||
mutex_lock(&ucsi->ppm_lock);
|
||||
ret = ucsi_acknowledge_connector_change(ucsi);
|
||||
mutex_unlock(&ucsi->ppm_lock);
|
||||
if (ret)
|
||||
dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
|
||||
|
||||
|
@ -25,6 +25,8 @@ struct ucsi_acpi {
|
||||
unsigned long flags;
|
||||
guid_t guid;
|
||||
u64 cmd;
|
||||
bool dell_quirk_probed;
|
||||
bool dell_quirk_active;
|
||||
};
|
||||
|
||||
static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
|
||||
@ -73,9 +75,13 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||
bool ack = UCSI_COMMAND(*(u64 *)val) == UCSI_ACK_CC_CI;
|
||||
int ret;
|
||||
|
||||
set_bit(COMMAND_PENDING, &ua->flags);
|
||||
if (ack)
|
||||
set_bit(ACK_PENDING, &ua->flags);
|
||||
else
|
||||
set_bit(COMMAND_PENDING, &ua->flags);
|
||||
|
||||
ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
|
||||
if (ret)
|
||||
@ -85,7 +91,10 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
out_clear_bit:
|
||||
clear_bit(COMMAND_PENDING, &ua->flags);
|
||||
if (ack)
|
||||
clear_bit(ACK_PENDING, &ua->flags);
|
||||
else
|
||||
clear_bit(COMMAND_PENDING, &ua->flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -119,12 +128,73 @@ static const struct ucsi_operations ucsi_zenbook_ops = {
|
||||
.async_write = ucsi_acpi_async_write
|
||||
};
|
||||
|
||||
static const struct dmi_system_id zenbook_dmi_id[] = {
|
||||
/*
|
||||
* Some Dell laptops expect that an ACK command with the
|
||||
* UCSI_ACK_CONNECTOR_CHANGE bit set is followed by a (separate)
|
||||
* ACK command that only has the UCSI_ACK_COMMAND_COMPLETE bit set.
|
||||
* If this is not done events are not delivered to OSPM and
|
||||
* subsequent commands will timeout.
|
||||
*/
|
||||
static int
|
||||
ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||
u64 cmd = *(u64 *)val, ack = 0;
|
||||
int ret;
|
||||
|
||||
if (UCSI_COMMAND(cmd) == UCSI_ACK_CC_CI &&
|
||||
cmd & UCSI_ACK_CONNECTOR_CHANGE)
|
||||
ack = UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE;
|
||||
|
||||
ret = ucsi_acpi_sync_write(ucsi, offset, val, val_len);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
if (ack == 0)
|
||||
return ret;
|
||||
|
||||
if (!ua->dell_quirk_probed) {
|
||||
ua->dell_quirk_probed = true;
|
||||
|
||||
cmd = UCSI_GET_CAPABILITY;
|
||||
ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd,
|
||||
sizeof(cmd));
|
||||
if (ret == 0)
|
||||
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL,
|
||||
&ack, sizeof(ack));
|
||||
if (ret != -ETIMEDOUT)
|
||||
return ret;
|
||||
|
||||
ua->dell_quirk_active = true;
|
||||
dev_err(ua->dev, "Firmware bug: Additional ACK required after ACKing a connector change.\n");
|
||||
dev_err(ua->dev, "Firmware bug: Enabling workaround\n");
|
||||
}
|
||||
|
||||
if (!ua->dell_quirk_active)
|
||||
return ret;
|
||||
|
||||
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &ack, sizeof(ack));
|
||||
}
|
||||
|
||||
static const struct ucsi_operations ucsi_dell_ops = {
|
||||
.read = ucsi_acpi_read,
|
||||
.sync_write = ucsi_dell_sync_write,
|
||||
.async_write = ucsi_acpi_async_write
|
||||
};
|
||||
|
||||
static const struct dmi_system_id ucsi_acpi_quirks[] = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"),
|
||||
},
|
||||
.driver_data = (void *)&ucsi_zenbook_ops,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
},
|
||||
.driver_data = (void *)&ucsi_dell_ops,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
@ -142,8 +212,10 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
|
||||
if (UCSI_CCI_CONNECTOR(cci))
|
||||
ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
|
||||
|
||||
if (test_bit(COMMAND_PENDING, &ua->flags) &&
|
||||
cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
|
||||
if (cci & UCSI_CCI_ACK_COMPLETE && test_bit(ACK_PENDING, &ua->flags))
|
||||
complete(&ua->complete);
|
||||
if (cci & UCSI_CCI_COMMAND_COMPLETE &&
|
||||
test_bit(COMMAND_PENDING, &ua->flags))
|
||||
complete(&ua->complete);
|
||||
}
|
||||
|
||||
@ -151,6 +223,7 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||
const struct ucsi_operations *ops = &ucsi_acpi_ops;
|
||||
const struct dmi_system_id *id;
|
||||
struct ucsi_acpi *ua;
|
||||
struct resource *res;
|
||||
acpi_status status;
|
||||
@ -180,8 +253,9 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
|
||||
init_completion(&ua->complete);
|
||||
ua->dev = &pdev->dev;
|
||||
|
||||
if (dmi_check_system(zenbook_dmi_id))
|
||||
ops = &ucsi_zenbook_ops;
|
||||
id = dmi_first_match(ucsi_acpi_quirks);
|
||||
if (id)
|
||||
ops = id->driver_data;
|
||||
|
||||
ua->ucsi = ucsi_create(&pdev->dev, ops);
|
||||
if (IS_ERR(ua->ucsi))
|
||||
|
Loading…
Reference in New Issue
Block a user