USB fixes for 6.14-rc6

Here are some small USB driver fixes for some reported issues for
 6.14-rc6.  These contain:
   - typec driver fixes
   - dwc3 driver fixes
   - xhci driver fixes
   - renesas controller fixes
   - gadget driver fixes
   - a new USB quirk added
 
 All of these have been in linux-next with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZ83RpQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ykjuwCgtiIGxLWcgV/ZjynWswrovKFX3akAnAh6kW1G
 +E7ta+MbasbGhI/Kp5pD
 =iCYy
 -----END PGP SIGNATURE-----

Merge tag 'usb-6.14-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB fixes from Greg KH:
 "Here are some small USB driver fixes for some reported issues. These
  contain:

   - typec driver fixes

   - dwc3 driver fixes

   - xhci driver fixes

   - renesas controller fixes

   - gadget driver fixes

   - a new USB quirk added

  All of these have been in linux-next with no reported issues"

* tag 'usb-6.14-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb:
  usb: typec: ucsi: Fix NULL pointer access
  usb: quirks: Add DELAY_INIT and NO_LPM for Prolific Mass Storage Card Reader
  usb: xhci: Fix host controllers "dying" after suspend and resume
  usb: dwc3: Set SUSPENDENABLE soon after phy init
  usb: hub: lack of clearing xHC resources
  usb: renesas_usbhs: Flush the notify_hotplug_work
  usb: renesas_usbhs: Use devm_usb_get_phy()
  usb: renesas_usbhs: Call clk_put()
  usb: dwc3: gadget: Prevent irq storm when TH re-executes
  usb: gadget: Check bmAttributes only if configuration is valid
  xhci: Restrict USB4 tunnel detection for USB3 devices to Intel hosts
  usb: xhci: Enable the TRB overfetch quirk on VIA VL805
  usb: gadget: Fix setting self-powered state on suspend
  usb: typec: ucsi: increase timeout for PPM reset operations
  acpi: typec: ucsi: Introduce a ->poll_cci method
  usb: typec: tcpci_rt1711h: Unmask alert interrupts to fix functionality
  usb: gadget: Set self-powered based on MaxPower and bmAttributes
  usb: gadget: u_ether: Set is_suspend flag if remote wakeup fails
  usb: atm: cxacru: fix a flaw in existing endpoint checks
This commit is contained in:
Linus Torvalds 2025-03-09 09:14:07 -10:00
commit 0dc1f314f8
24 changed files with 189 additions and 83 deletions

View File

@ -1131,7 +1131,10 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
struct cxacru_data *instance; struct cxacru_data *instance;
struct usb_device *usb_dev = interface_to_usbdev(intf); struct usb_device *usb_dev = interface_to_usbdev(intf);
struct usb_host_endpoint *cmd_ep = usb_dev->ep_in[CXACRU_EP_CMD]; struct usb_host_endpoint *cmd_ep = usb_dev->ep_in[CXACRU_EP_CMD];
struct usb_endpoint_descriptor *in, *out; static const u8 ep_addrs[] = {
CXACRU_EP_CMD + USB_DIR_IN,
CXACRU_EP_CMD + USB_DIR_OUT,
0};
int ret; int ret;
/* instance init */ /* instance init */
@ -1179,13 +1182,11 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
} }
if (usb_endpoint_xfer_int(&cmd_ep->desc)) if (usb_endpoint_xfer_int(&cmd_ep->desc))
ret = usb_find_common_endpoints(intf->cur_altsetting, ret = usb_check_int_endpoints(intf, ep_addrs);
NULL, NULL, &in, &out);
else else
ret = usb_find_common_endpoints(intf->cur_altsetting, ret = usb_check_bulk_endpoints(intf, ep_addrs);
&in, &out, NULL, NULL);
if (ret) { if (!ret) {
usb_err(usbatm_instance, "cxacru_bind: interface has incorrect endpoints\n"); usb_err(usbatm_instance, "cxacru_bind: interface has incorrect endpoints\n");
ret = -ENODEV; ret = -ENODEV;
goto fail; goto fail;

View File

@ -6065,6 +6065,36 @@ void usb_hub_cleanup(void)
usb_deregister(&hub_driver); usb_deregister(&hub_driver);
} /* usb_hub_cleanup() */ } /* usb_hub_cleanup() */
/**
* hub_hc_release_resources - clear resources used by host controller
* @udev: pointer to device being released
*
* Context: task context, might sleep
*
* Function releases the host controller resources in correct order before
* making any operation on resuming usb device. The host controller resources
* allocated for devices in tree should be released starting from the last
* usb device in tree toward the root hub. This function is used only during
* resuming device when usb device require reinitialization that is, when
* flag udev->reset_resume is set.
*
* This call is synchronous, and may not be used in an interrupt context.
*/
static void hub_hc_release_resources(struct usb_device *udev)
{
struct usb_hub *hub = usb_hub_to_struct_hub(udev);
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
int i;
/* Release up resources for all children before this device */
for (i = 0; i < udev->maxchild; i++)
if (hub->ports[i]->child)
hub_hc_release_resources(hub->ports[i]->child);
if (hcd->driver->reset_device)
hcd->driver->reset_device(hcd, udev);
}
/** /**
* usb_reset_and_verify_device - perform a USB port reset to reinitialize a device * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device
* @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
@ -6129,6 +6159,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
bos = udev->bos; bos = udev->bos;
udev->bos = NULL; udev->bos = NULL;
if (udev->reset_resume)
hub_hc_release_resources(udev);
mutex_lock(hcd->address0_mutex); mutex_lock(hcd->address0_mutex);
for (i = 0; i < PORT_INIT_TRIES; ++i) { for (i = 0; i < PORT_INIT_TRIES; ++i) {

View File

@ -341,6 +341,10 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0638, 0x0a13), .driver_info = { USB_DEVICE(0x0638, 0x0a13), .driver_info =
USB_QUIRK_STRING_FETCH_255 }, USB_QUIRK_STRING_FETCH_255 },
/* Prolific Single-LUN Mass Storage Card Reader */
{ USB_DEVICE(0x067b, 0x2731), .driver_info = USB_QUIRK_DELAY_INIT |
USB_QUIRK_NO_LPM },
/* Saitek Cyborg Gold Joystick */ /* Saitek Cyborg Gold Joystick */
{ USB_DEVICE(0x06a3, 0x0006), .driver_info = { USB_DEVICE(0x06a3, 0x0006), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS }, USB_QUIRK_CONFIG_INTF_STRINGS },

View File

@ -131,11 +131,24 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
} }
} }
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
{ {
unsigned int hw_mode;
u32 reg; u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg = dwc3_readl(dwc->regs, DWC3_GCTL);
/*
* For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and
* GUSB2PHYCFG.SUSPHY should be cleared during mode switching,
* and they can be set after core initialization.
*/
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && !ignore_susphy) {
if (DWC3_GCTL_PRTCAP(reg) != mode)
dwc3_enable_susphy(dwc, false);
}
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
reg |= DWC3_GCTL_PRTCAPDIR(mode); reg |= DWC3_GCTL_PRTCAPDIR(mode);
dwc3_writel(dwc->regs, DWC3_GCTL, reg); dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@ -216,7 +229,7 @@ static void __dwc3_set_mode(struct work_struct *work)
spin_lock_irqsave(&dwc->lock, flags); spin_lock_irqsave(&dwc->lock, flags);
dwc3_set_prtcap(dwc, desired_dr_role); dwc3_set_prtcap(dwc, desired_dr_role, false);
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
@ -658,16 +671,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
*/ */
reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX; reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
/* /* Ensure the GUSB3PIPECTL.SUSPENDENABLE is cleared prior to phy init. */
* Above DWC_usb3.0 1.94a, it is recommended to set
* DWC3_GUSB3PIPECTL_SUSPHY to '0' during coreConsultant configuration.
* So default value will be '0' when the core is reset. Application
* needs to set it to '1' after the core initialization is completed.
*
* Similarly for DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be
* cleared after power-on reset, and it can be set after core
* initialization.
*/
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
if (dwc->u2ss_inp3_quirk) if (dwc->u2ss_inp3_quirk)
@ -747,15 +751,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
break; break;
} }
/* /* Ensure the GUSB2PHYCFG.SUSPHY is cleared prior to phy init. */
* Above DWC_usb3.0 1.94a, it is recommended to set
* DWC3_GUSB2PHYCFG_SUSPHY to '0' during coreConsultant configuration.
* So default value will be '0' when the core is reset. Application
* needs to set it to '1' after the core initialization is completed.
*
* Similarly for DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared
* after power-on reset, and it can be set after core initialization.
*/
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
if (dwc->dis_enblslpm_quirk) if (dwc->dis_enblslpm_quirk)
@ -830,6 +826,25 @@ static int dwc3_phy_init(struct dwc3 *dwc)
goto err_exit_usb3_phy; goto err_exit_usb3_phy;
} }
/*
* Above DWC_usb3.0 1.94a, it is recommended to set
* DWC3_GUSB3PIPECTL_SUSPHY and DWC3_GUSB2PHYCFG_SUSPHY to '0' during
* coreConsultant configuration. So default value will be '0' when the
* core is reset. Application needs to set it to '1' after the core
* initialization is completed.
*
* Certain phy requires to be in P0 power state during initialization.
* Make sure GUSB3PIPECTL.SUSPENDENABLE and GUSB2PHYCFG.SUSPHY are clear
* prior to phy init to maintain in the P0 state.
*
* After phy initialization, some phy operations can only be executed
* while in lower P states. Ensure GUSB3PIPECTL.SUSPENDENABLE and
* GUSB2PHYCFG.SUSPHY are set soon after initialization to avoid
* blocking phy ops.
*/
if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
dwc3_enable_susphy(dwc, true);
return 0; return 0;
err_exit_usb3_phy: err_exit_usb3_phy:
@ -1588,7 +1603,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) { switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_PERIPHERAL:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, false);
if (dwc->usb2_phy) if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false); otg_set_vbus(dwc->usb2_phy->otg, false);
@ -1600,7 +1615,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
return dev_err_probe(dev, ret, "failed to initialize gadget\n"); return dev_err_probe(dev, ret, "failed to initialize gadget\n");
break; break;
case USB_DR_MODE_HOST: case USB_DR_MODE_HOST:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, false);
if (dwc->usb2_phy) if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true); otg_set_vbus(dwc->usb2_phy->otg, true);
@ -1645,7 +1660,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
} }
/* de-assert DRVVBUS for HOST and OTG mode */ /* de-assert DRVVBUS for HOST and OTG mode */
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
} }
static void dwc3_get_software_properties(struct dwc3 *dwc) static void dwc3_get_software_properties(struct dwc3 *dwc)
@ -1835,8 +1850,6 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd; dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd;
dwc->tx_max_burst_prd = tx_max_burst_prd; dwc->tx_max_burst_prd = tx_max_burst_prd;
dwc->imod_interval = 0;
dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num; dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
} }
@ -1854,21 +1867,19 @@ static void dwc3_check_params(struct dwc3 *dwc)
unsigned int hwparam_gen = unsigned int hwparam_gen =
DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3); DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3);
/* Check for proper value of imod_interval */
if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
dev_warn(dwc->dev, "Interrupt moderation not supported\n");
dwc->imod_interval = 0;
}
/* /*
* Enable IMOD for all supporting controllers.
*
* Particularly, DWC_usb3 v3.00a must enable this feature for
* the following reason:
*
* Workaround for STAR 9000961433 which affects only version * Workaround for STAR 9000961433 which affects only version
* 3.00a of the DWC_usb3 core. This prevents the controller * 3.00a of the DWC_usb3 core. This prevents the controller
* interrupt from being masked while handling events. IMOD * interrupt from being masked while handling events. IMOD
* allows us to work around this issue. Enable it for the * allows us to work around this issue. Enable it for the
* affected version. * affected version.
*/ */
if (!dwc->imod_interval && if (dwc3_has_imod((dwc)))
DWC3_VER_IS(DWC3, 300A))
dwc->imod_interval = 1; dwc->imod_interval = 1;
/* Check the maximum_speed parameter */ /* Check the maximum_speed parameter */
@ -2457,7 +2468,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (ret) if (ret)
return ret; return ret;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
dwc3_gadget_resume(dwc); dwc3_gadget_resume(dwc);
break; break;
case DWC3_GCTL_PRTCAP_HOST: case DWC3_GCTL_PRTCAP_HOST:
@ -2465,7 +2476,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
ret = dwc3_core_init_for_resume(dwc); ret = dwc3_core_init_for_resume(dwc);
if (ret) if (ret)
return ret; return ret;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, true);
break; break;
} }
/* Restore GUSB2PHYCFG bits that were modified in suspend */ /* Restore GUSB2PHYCFG bits that were modified in suspend */
@ -2494,7 +2505,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (ret) if (ret)
return ret; return ret;
dwc3_set_prtcap(dwc, dwc->current_dr_role); dwc3_set_prtcap(dwc, dwc->current_dr_role, true);
dwc3_otg_init(dwc); dwc3_otg_init(dwc);
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) { if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {

View File

@ -1558,7 +1558,7 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3) #define DWC3_HAS_OTG BIT(3)
/* prototypes */ /* prototypes */
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode); void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);

View File

@ -173,7 +173,7 @@ void dwc3_otg_init(struct dwc3 *dwc)
* block "Initialize GCTL for OTG operation". * block "Initialize GCTL for OTG operation".
*/ */
/* GCTL.PrtCapDir=2'b11 */ /* GCTL.PrtCapDir=2'b11 */
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);
/* GUSB2PHYCFG0.SusPHY=0 */ /* GUSB2PHYCFG0.SusPHY=0 */
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
@ -556,7 +556,7 @@ int dwc3_drd_init(struct dwc3 *dwc)
dwc3_drd_update(dwc); dwc3_drd_update(dwc);
} else { } else {
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);
/* use OTG block to get ID event */ /* use OTG block to get ID event */
irq = dwc3_otg_get_irq(dwc); irq = dwc3_otg_get_irq(dwc);

View File

@ -4501,14 +4501,18 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_SIZE(evt->length)); DWC3_GEVNTSIZ_SIZE(evt->length));
evt->flags &= ~DWC3_EVENT_PENDING;
/*
* Add an explicit write memory barrier to make sure that the update of
* clearing DWC3_EVENT_PENDING is observed in dwc3_check_event_buf()
*/
wmb();
if (dwc->imod_interval) { if (dwc->imod_interval) {
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
} }
/* Keep the clearing of DWC3_EVENT_PENDING at the end */
evt->flags &= ~DWC3_EVENT_PENDING;
return ret; return ret;
} }

View File

@ -1050,10 +1050,11 @@ static int set_config(struct usb_composite_dev *cdev,
else else
usb_gadget_set_remote_wakeup(gadget, 0); usb_gadget_set_remote_wakeup(gadget, 0);
done: done:
if (power <= USB_SELF_POWER_VBUS_MAX_DRAW) if (power > USB_SELF_POWER_VBUS_MAX_DRAW ||
usb_gadget_set_selfpowered(gadget); (c && !(c->bmAttributes & USB_CONFIG_ATT_SELFPOWER)))
else
usb_gadget_clear_selfpowered(gadget); usb_gadget_clear_selfpowered(gadget);
else
usb_gadget_set_selfpowered(gadget);
usb_gadget_vbus_draw(gadget, power); usb_gadget_vbus_draw(gadget, power);
if (result >= 0 && cdev->delayed_status) if (result >= 0 && cdev->delayed_status)
@ -2615,7 +2616,10 @@ void composite_suspend(struct usb_gadget *gadget)
cdev->suspended = 1; cdev->suspended = 1;
usb_gadget_set_selfpowered(gadget); if (cdev->config &&
cdev->config->bmAttributes & USB_CONFIG_ATT_SELFPOWER)
usb_gadget_set_selfpowered(gadget);
usb_gadget_vbus_draw(gadget, 2); usb_gadget_vbus_draw(gadget, 2);
} }
@ -2649,8 +2653,11 @@ void composite_resume(struct usb_gadget *gadget)
else else
maxpower = min(maxpower, 900U); maxpower = min(maxpower, 900U);
if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW) if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW ||
!(cdev->config->bmAttributes & USB_CONFIG_ATT_SELFPOWER))
usb_gadget_clear_selfpowered(gadget); usb_gadget_clear_selfpowered(gadget);
else
usb_gadget_set_selfpowered(gadget);
usb_gadget_vbus_draw(gadget, maxpower); usb_gadget_vbus_draw(gadget, maxpower);
} else { } else {

View File

@ -1052,8 +1052,8 @@ void gether_suspend(struct gether *link)
* There is a transfer in progress. So we trigger a remote * There is a transfer in progress. So we trigger a remote
* wakeup to inform the host. * wakeup to inform the host.
*/ */
ether_wakeup_host(dev->port_usb); if (!ether_wakeup_host(dev->port_usb))
return; return;
} }
spin_lock_irqsave(&dev->lock, flags); spin_lock_irqsave(&dev->lock, flags);
link->is_suspend = true; link->is_suspend = true;

View File

@ -12,6 +12,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/unaligned.h> #include <linux/unaligned.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/pci.h>
#include "xhci.h" #include "xhci.h"
#include "xhci-trace.h" #include "xhci-trace.h"
@ -770,9 +771,16 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci, enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
struct xhci_port *port) struct xhci_port *port)
{ {
struct usb_hcd *hcd;
void __iomem *base; void __iomem *base;
u32 offset; u32 offset;
/* Don't try and probe this capability for non-Intel hosts */
hcd = xhci_to_hcd(xhci);
if (!dev_is_pci(hcd->self.controller) ||
to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL)
return USB_LINK_UNKNOWN;
base = &xhci->cap_regs->hc_capbase; base = &xhci->cap_regs->hc_capbase;
offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW); offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);

View File

@ -2437,7 +2437,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
* and our use of dma addresses in the trb_address_map radix tree needs * and our use of dma addresses in the trb_address_map radix tree needs
* TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need. * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need.
*/ */
if (xhci->quirks & XHCI_ZHAOXIN_TRB_FETCH) if (xhci->quirks & XHCI_TRB_OVERFETCH)
/* Buggy HC prefetches beyond segment bounds - allocate dummy space at the end */
xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
TRB_SEGMENT_SIZE * 2, TRB_SEGMENT_SIZE * 2, xhci->page_size * 2); TRB_SEGMENT_SIZE * 2, TRB_SEGMENT_SIZE * 2, xhci->page_size * 2);
else else

View File

@ -38,6 +38,8 @@
#define PCI_DEVICE_ID_ETRON_EJ168 0x7023 #define PCI_DEVICE_ID_ETRON_EJ168 0x7023
#define PCI_DEVICE_ID_ETRON_EJ188 0x7052 #define PCI_DEVICE_ID_ETRON_EJ188 0x7052
#define PCI_DEVICE_ID_VIA_VL805 0x3483
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1
@ -418,8 +420,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == 0x3432) pdev->device == 0x3432)
xhci->quirks |= XHCI_BROKEN_STREAMS; xhci->quirks |= XHCI_BROKEN_STREAMS;
if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == PCI_DEVICE_ID_VIA_VL805) {
xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_LPM_SUPPORT;
xhci->quirks |= XHCI_TRB_OVERFETCH;
}
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
pdev->device == PCI_DEVICE_ID_ASMEDIA_1042_XHCI) { pdev->device == PCI_DEVICE_ID_ASMEDIA_1042_XHCI) {
@ -467,11 +471,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->device == 0x9202) { if (pdev->device == 0x9202) {
xhci->quirks |= XHCI_RESET_ON_RESUME; xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; xhci->quirks |= XHCI_TRB_OVERFETCH;
} }
if (pdev->device == 0x9203) if (pdev->device == 0x9203)
xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; xhci->quirks |= XHCI_TRB_OVERFETCH;
} }
if (pdev->vendor == PCI_VENDOR_ID_CDNS && if (pdev->vendor == PCI_VENDOR_ID_CDNS &&

View File

@ -780,8 +780,12 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci)
struct xhci_segment *seg; struct xhci_segment *seg;
ring = xhci->cmd_ring; ring = xhci->cmd_ring;
xhci_for_each_ring_seg(ring->first_seg, seg) xhci_for_each_ring_seg(ring->first_seg, seg) {
/* erase all TRBs before the link */
memset(seg->trbs, 0, sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1)); memset(seg->trbs, 0, sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1));
/* clear link cycle bit */
seg->trbs[TRBS_PER_SEGMENT - 1].link.control &= cpu_to_le32(~TRB_CYCLE);
}
xhci_initialize_ring_info(ring); xhci_initialize_ring_info(ring);
/* /*

View File

@ -1632,7 +1632,7 @@ struct xhci_hcd {
#define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42) #define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42)
#define XHCI_SUSPEND_RESUME_CLKS BIT_ULL(43) #define XHCI_SUSPEND_RESUME_CLKS BIT_ULL(43)
#define XHCI_RESET_TO_DEFAULT BIT_ULL(44) #define XHCI_RESET_TO_DEFAULT BIT_ULL(44)
#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45) #define XHCI_TRB_OVERFETCH BIT_ULL(45)
#define XHCI_ZHAOXIN_HOST BIT_ULL(46) #define XHCI_ZHAOXIN_HOST BIT_ULL(46)
#define XHCI_WRITE_64_HI_LO BIT_ULL(47) #define XHCI_WRITE_64_HI_LO BIT_ULL(47)
#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48) #define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48)

View File

@ -312,8 +312,10 @@ static int usbhsc_clk_get(struct device *dev, struct usbhs_priv *priv)
priv->clks[1] = of_clk_get(dev_of_node(dev), 1); priv->clks[1] = of_clk_get(dev_of_node(dev), 1);
if (PTR_ERR(priv->clks[1]) == -ENOENT) if (PTR_ERR(priv->clks[1]) == -ENOENT)
priv->clks[1] = NULL; priv->clks[1] = NULL;
else if (IS_ERR(priv->clks[1])) else if (IS_ERR(priv->clks[1])) {
clk_put(priv->clks[0]);
return PTR_ERR(priv->clks[1]); return PTR_ERR(priv->clks[1]);
}
return 0; return 0;
} }
@ -779,6 +781,8 @@ static void usbhs_remove(struct platform_device *pdev)
dev_dbg(&pdev->dev, "usb remove\n"); dev_dbg(&pdev->dev, "usb remove\n");
flush_delayed_work(&priv->notify_hotplug_work);
/* power off */ /* power off */
if (!usbhs_get_dparam(priv, runtime_pwctrl)) if (!usbhs_get_dparam(priv, runtime_pwctrl))
usbhsc_power_ctrl(priv, 0); usbhsc_power_ctrl(priv, 0);

View File

@ -1094,7 +1094,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
goto usbhs_mod_gadget_probe_err_gpriv; goto usbhs_mod_gadget_probe_err_gpriv;
} }
gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED); gpriv->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_UNDEFINED);
dev_info(dev, "%stransceiver found\n", dev_info(dev, "%stransceiver found\n",
!IS_ERR(gpriv->transceiver) ? "" : "no "); !IS_ERR(gpriv->transceiver) ? "" : "no ");

View File

@ -334,6 +334,11 @@ static int rt1711h_probe(struct i2c_client *client)
{ {
int ret; int ret;
struct rt1711h_chip *chip; struct rt1711h_chip *chip;
const u16 alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED |
TCPC_ALERT_TX_FAILED | TCPC_ALERT_RX_HARD_RST |
TCPC_ALERT_RX_STATUS | TCPC_ALERT_POWER_STATUS |
TCPC_ALERT_CC_STATUS | TCPC_ALERT_RX_BUF_OVF |
TCPC_ALERT_FAULT;
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip) if (!chip)
@ -382,6 +387,12 @@ static int rt1711h_probe(struct i2c_client *client)
dev_name(chip->dev), chip); dev_name(chip->dev), chip);
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Enable alert interrupts */
ret = rt1711h_write16(chip, TCPC_ALERT_MASK, alert_mask);
if (ret < 0)
return ret;
enable_irq_wake(client->irq); enable_irq_wake(client->irq);
return 0; return 0;

View File

@ -25,7 +25,7 @@
* difficult to estimate the time it takes for the system to process the command * difficult to estimate the time it takes for the system to process the command
* before it is actually passed to the PPM. * before it is actually passed to the PPM.
*/ */
#define UCSI_TIMEOUT_MS 5000 #define UCSI_TIMEOUT_MS 10000
/* /*
* UCSI_SWAP_TIMEOUT_MS - Timeout for role swap requests * UCSI_SWAP_TIMEOUT_MS - Timeout for role swap requests
@ -1346,7 +1346,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
mutex_lock(&ucsi->ppm_lock); mutex_lock(&ucsi->ppm_lock);
ret = ucsi->ops->read_cci(ucsi, &cci); ret = ucsi->ops->poll_cci(ucsi, &cci);
if (ret < 0) if (ret < 0)
goto out; goto out;
@ -1364,7 +1364,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS); tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
do { do {
ret = ucsi->ops->read_cci(ucsi, &cci); ret = ucsi->ops->poll_cci(ucsi, &cci);
if (ret < 0) if (ret < 0)
goto out; goto out;
if (cci & UCSI_CCI_COMMAND_COMPLETE) if (cci & UCSI_CCI_COMMAND_COMPLETE)
@ -1393,7 +1393,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
/* Give the PPM time to process a reset before reading CCI */ /* Give the PPM time to process a reset before reading CCI */
msleep(20); msleep(20);
ret = ucsi->ops->read_cci(ucsi, &cci); ret = ucsi->ops->poll_cci(ucsi, &cci);
if (ret) if (ret)
goto out; goto out;
@ -1825,11 +1825,11 @@ static int ucsi_init(struct ucsi *ucsi)
err_unregister: err_unregister:
for (con = connector; con->port; con++) { for (con = connector; con->port; con++) {
if (con->wq)
destroy_workqueue(con->wq);
ucsi_unregister_partner(con); ucsi_unregister_partner(con);
ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
ucsi_unregister_port_psy(con); ucsi_unregister_port_psy(con);
if (con->wq)
destroy_workqueue(con->wq);
usb_power_delivery_unregister_capabilities(con->port_sink_caps); usb_power_delivery_unregister_capabilities(con->port_sink_caps);
con->port_sink_caps = NULL; con->port_sink_caps = NULL;
@ -1929,8 +1929,8 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
struct ucsi *ucsi; struct ucsi *ucsi;
if (!ops || if (!ops ||
!ops->read_version || !ops->read_cci || !ops->read_message_in || !ops->read_version || !ops->read_cci || !ops->poll_cci ||
!ops->sync_control || !ops->async_control) !ops->read_message_in || !ops->sync_control || !ops->async_control)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL); ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
@ -2013,10 +2013,6 @@ void ucsi_unregister(struct ucsi *ucsi)
for (i = 0; i < ucsi->cap.num_connectors; i++) { for (i = 0; i < ucsi->cap.num_connectors; i++) {
cancel_work_sync(&ucsi->connector[i].work); cancel_work_sync(&ucsi->connector[i].work);
ucsi_unregister_partner(&ucsi->connector[i]);
ucsi_unregister_altmodes(&ucsi->connector[i],
UCSI_RECIPIENT_CON);
ucsi_unregister_port_psy(&ucsi->connector[i]);
if (ucsi->connector[i].wq) { if (ucsi->connector[i].wq) {
struct ucsi_work *uwork; struct ucsi_work *uwork;
@ -2032,6 +2028,11 @@ void ucsi_unregister(struct ucsi *ucsi)
destroy_workqueue(ucsi->connector[i].wq); destroy_workqueue(ucsi->connector[i].wq);
} }
ucsi_unregister_partner(&ucsi->connector[i]);
ucsi_unregister_altmodes(&ucsi->connector[i],
UCSI_RECIPIENT_CON);
ucsi_unregister_port_psy(&ucsi->connector[i]);
usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps); usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps);
ucsi->connector[i].port_sink_caps = NULL; ucsi->connector[i].port_sink_caps = NULL;
usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps); usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps);

View File

@ -62,6 +62,7 @@ struct dentry;
* struct ucsi_operations - UCSI I/O operations * struct ucsi_operations - UCSI I/O operations
* @read_version: Read implemented UCSI version * @read_version: Read implemented UCSI version
* @read_cci: Read CCI register * @read_cci: Read CCI register
* @poll_cci: Read CCI register while polling with notifications disabled
* @read_message_in: Read message data from UCSI * @read_message_in: Read message data from UCSI
* @sync_control: Blocking control operation * @sync_control: Blocking control operation
* @async_control: Non-blocking control operation * @async_control: Non-blocking control operation
@ -76,6 +77,7 @@ struct dentry;
struct ucsi_operations { struct ucsi_operations {
int (*read_version)(struct ucsi *ucsi, u16 *version); int (*read_version)(struct ucsi *ucsi, u16 *version);
int (*read_cci)(struct ucsi *ucsi, u32 *cci); int (*read_cci)(struct ucsi *ucsi, u32 *cci);
int (*poll_cci)(struct ucsi *ucsi, u32 *cci);
int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len); int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len);
int (*sync_control)(struct ucsi *ucsi, u64 command); int (*sync_control)(struct ucsi *ucsi, u64 command);
int (*async_control)(struct ucsi *ucsi, u64 command); int (*async_control)(struct ucsi *ucsi, u64 command);

View File

@ -59,19 +59,24 @@ static int ucsi_acpi_read_version(struct ucsi *ucsi, u16 *version)
static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci) static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci)
{ {
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) {
ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
if (ret)
return ret;
}
memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci)); memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));
return 0; return 0;
} }
static int ucsi_acpi_poll_cci(struct ucsi *ucsi, u32 *cci)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
if (ret)
return ret;
return ucsi_acpi_read_cci(ucsi, cci);
}
static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
{ {
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
@ -94,6 +99,7 @@ static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command)
static const struct ucsi_operations ucsi_acpi_ops = { static const struct ucsi_operations ucsi_acpi_ops = {
.read_version = ucsi_acpi_read_version, .read_version = ucsi_acpi_read_version,
.read_cci = ucsi_acpi_read_cci, .read_cci = ucsi_acpi_read_cci,
.poll_cci = ucsi_acpi_poll_cci,
.read_message_in = ucsi_acpi_read_message_in, .read_message_in = ucsi_acpi_read_message_in,
.sync_control = ucsi_sync_control_common, .sync_control = ucsi_sync_control_common,
.async_control = ucsi_acpi_async_control .async_control = ucsi_acpi_async_control
@ -142,6 +148,7 @@ static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
static const struct ucsi_operations ucsi_gram_ops = { static const struct ucsi_operations ucsi_gram_ops = {
.read_version = ucsi_acpi_read_version, .read_version = ucsi_acpi_read_version,
.read_cci = ucsi_acpi_read_cci, .read_cci = ucsi_acpi_read_cci,
.poll_cci = ucsi_acpi_poll_cci,
.read_message_in = ucsi_gram_read_message_in, .read_message_in = ucsi_gram_read_message_in,
.sync_control = ucsi_gram_sync_control, .sync_control = ucsi_gram_sync_control,
.async_control = ucsi_acpi_async_control .async_control = ucsi_acpi_async_control

View File

@ -664,6 +664,7 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
static const struct ucsi_operations ucsi_ccg_ops = { static const struct ucsi_operations ucsi_ccg_ops = {
.read_version = ucsi_ccg_read_version, .read_version = ucsi_ccg_read_version,
.read_cci = ucsi_ccg_read_cci, .read_cci = ucsi_ccg_read_cci,
.poll_cci = ucsi_ccg_read_cci,
.read_message_in = ucsi_ccg_read_message_in, .read_message_in = ucsi_ccg_read_message_in,
.sync_control = ucsi_ccg_sync_control, .sync_control = ucsi_ccg_sync_control,
.async_control = ucsi_ccg_async_control, .async_control = ucsi_ccg_async_control,

View File

@ -206,6 +206,7 @@ static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con)
static const struct ucsi_operations pmic_glink_ucsi_ops = { static const struct ucsi_operations pmic_glink_ucsi_ops = {
.read_version = pmic_glink_ucsi_read_version, .read_version = pmic_glink_ucsi_read_version,
.read_cci = pmic_glink_ucsi_read_cci, .read_cci = pmic_glink_ucsi_read_cci,
.poll_cci = pmic_glink_ucsi_read_cci,
.read_message_in = pmic_glink_ucsi_read_message_in, .read_message_in = pmic_glink_ucsi_read_message_in,
.sync_control = ucsi_sync_control_common, .sync_control = ucsi_sync_control_common,
.async_control = pmic_glink_ucsi_async_control, .async_control = pmic_glink_ucsi_async_control,

View File

@ -424,6 +424,7 @@ static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data)
static const struct ucsi_operations ucsi_stm32g0_ops = { static const struct ucsi_operations ucsi_stm32g0_ops = {
.read_version = ucsi_stm32g0_read_version, .read_version = ucsi_stm32g0_read_version,
.read_cci = ucsi_stm32g0_read_cci, .read_cci = ucsi_stm32g0_read_cci,
.poll_cci = ucsi_stm32g0_read_cci,
.read_message_in = ucsi_stm32g0_read_message_in, .read_message_in = ucsi_stm32g0_read_message_in,
.sync_control = ucsi_sync_control_common, .sync_control = ucsi_sync_control_common,
.async_control = ucsi_stm32g0_async_control, .async_control = ucsi_stm32g0_async_control,

View File

@ -74,6 +74,7 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
static const struct ucsi_operations yoga_c630_ucsi_ops = { static const struct ucsi_operations yoga_c630_ucsi_ops = {
.read_version = yoga_c630_ucsi_read_version, .read_version = yoga_c630_ucsi_read_version,
.read_cci = yoga_c630_ucsi_read_cci, .read_cci = yoga_c630_ucsi_read_cci,
.poll_cci = yoga_c630_ucsi_read_cci,
.read_message_in = yoga_c630_ucsi_read_message_in, .read_message_in = yoga_c630_ucsi_read_message_in,
.sync_control = ucsi_sync_control_common, .sync_control = ucsi_sync_control_common,
.async_control = yoga_c630_ucsi_async_control, .async_control = yoga_c630_ucsi_async_control,