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

Pull USB fixes from Greg KH:
 "Here are a number of small USB driver fixes for many reported issues.
  Included in here are:

   - usb serial driver corruption and use-after-free fixes

   - usb gadget rndis bugfixes for malicious/buggy host connections

   - typec driver fixes for a load of different tiny reported issues

   - typec mux driver revert for a broken patch in -rc1

   - usb gadget driver fixes for many different reported problems

   - new usb device quirks added

   - usbip tool fixes and some core usbip fixes as well

   - dwc3 driver fixes for minor issues

   - xhci driver fixes for reported problems

   - lots of other tiny usb driver fixes for many tiny issues

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

* tag 'usb-7.2-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (56 commits)
  USB: core: ratelimit cabling message
  usb: misc: usbio: fix disconnect UAF in client teardown
  Revert "usb: typec: mux: avoid duplicated mux switches"
  USB: chaoskey: Fix slab-use-after-free in chaoskey_release()
  usb: ucsi: huawei_gaokun: move typec_altmode off stack
  usb: typec: tcpci_rt1711h: unregister TCPCI port with devres
  usb: typec: tcpm: Fix VDM type for Enter Mode commands
  usb: typec: ucsi: cancel pending work on system suspend
  usb: typec: class: drop PD lookup reference
  usb: typec: ps883x: Fix DP+USB3 configuration
  usb: xhci: Fix sleep in atomic context in xhci_free_streams()
  xhci: sideband: fix ring sg table pages leak
  usb: gadget: udc: Fix use-after-free in gadget_match_driver
  usb: dwc3: run gadget disconnect from sleepable suspend context
  usb: sl811-hcd: disable controller wakeup on remove
  usb: typec: anx7411: use devm_pm_runtime_enable()
  usb: dwc3: fix dwc3_readl() and dwc3_writel() calls in dwc3_ulpi_setup()
  USB: misc: uss720: unregister parport on probe failure
  usb: gadget: function: rndis: add length check for header
  usb: gadget: function: rndis: add length check to response query
  ...
This commit is contained in:
Linus Torvalds committed 2026-07-12 12:12:41 -07:00
commit 534f8f051e
53 files changed
+463 -205

No files matched your search

@@ -22,7 +22,7 @@ Description:
Reading this attribute gives the state of the DbC. It
can be one of the following states: disabled, enabled,
initialized, connected or configured.
initialized, connected, configured or suspended.
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_idVendor
Date: March 2023
+1
View File
@@ -28055,6 +28055,7 @@ F: include/dt-bindings/usb/
F: include/linux/usb.h
F: include/linux/usb/
F: include/uapi/linux/usb/
F: rust/kernel/usb.rs
USB TYPEC BUS FOR ALTERNATE MODES
M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+31 -5
View File
@@ -594,7 +594,9 @@ static int uea_send_modem_cmd(struct usb_device *usb,
static void uea_upload_pre_firmware(const struct firmware *fw_entry,
void *context)
{
struct usb_device *usb = context;
struct usb_interface *intf = context;
struct usb_device *usb = interface_to_usbdev(intf);
struct completion *fw_done = usb_get_intfdata(intf);
const u8 *pfw;
u8 value;
u32 crc = 0;
@@ -663,15 +665,17 @@ err_fw_corrupted:
uea_err(usb, "firmware is corrupted\n");
err:
release_firmware(fw_entry);
complete(fw_done);
}
/*
* uea_load_firmware - Load usb firmware for pre-firmware devices.
*/
static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
static int uea_load_firmware(struct usb_interface *intf, unsigned int ver)
{
int ret;
char *fw_name = EAGLE_FIRMWARE;
struct usb_device *usb = interface_to_usbdev(intf);
uea_info(usb, "pre-firmware device, uploading firmware\n");
@@ -694,7 +698,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
}
ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev,
GFP_KERNEL, usb,
GFP_KERNEL, intf,
uea_upload_pre_firmware);
if (ret)
uea_err(usb, "firmware %s is not available\n", fw_name);
@@ -2555,8 +2559,23 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
usb_reset_device(usb);
if (UEA_IS_PREFIRM(id))
return uea_load_firmware(usb, UEA_CHIP_VERSION(id));
if (UEA_IS_PREFIRM(id)) {
struct completion *fw_done;
/* Wait for the firmware load to be done, in .disconnect() */
fw_done = kzalloc_obj(*fw_done);
if (!fw_done)
return -ENOMEM;
init_completion(fw_done);
usb_set_intfdata(intf, fw_done);
ret = uea_load_firmware(intf, UEA_CHIP_VERSION(id));
if (ret)
kfree(fw_done);
return ret;
}
ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver);
if (ret == 0) {
@@ -2586,6 +2605,13 @@ static void uea_disconnect(struct usb_interface *intf)
usbatm_usb_disconnect(intf);
mutex_unlock(&uea_mutex);
uea_info(usb, "ADSL device removed\n");
} else if (usb->config->desc.bNumInterfaces == 1) {
struct completion *fw_done = usb_get_intfdata(intf);
uea_dbg(usb, "pre-firmware device, waiting firmware upload\n");
wait_for_completion(fw_done);
uea_dbg(usb, "pre-firmware device, finished waiting\n");
kfree(fw_done);
}
}
+2
View File
@@ -631,6 +631,8 @@ cleanup_rings:
}
}
cdnsp_free_stream_ctx(pdev, pep);
cleanup_stream_rings:
kfree(pep->stream_info.stream_rings);
+3
View File
@@ -1816,6 +1816,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */
.driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */
},
{ USB_DEVICE(0x1965, 0x0017), /* Uniden BC125AT */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
{ USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
+10 -13
View File
@@ -281,28 +281,24 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
ulpi->dev.parent = dev; /* needed early for ops */
ulpi->dev.bus = &ulpi_bus;
ulpi->dev.type = &ulpi_dev_type;
device_initialize(&ulpi->dev);
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
ret = ulpi_of_register(ulpi);
if (ret) {
kfree(ulpi);
if (ret)
return ret;
}
ret = ulpi_read_id(ulpi);
if (ret) {
of_node_put(ulpi->dev.of_node);
kfree(ulpi);
if (ret)
return ret;
}
ret = device_register(&ulpi->dev);
if (ret) {
put_device(&ulpi->dev);
ret = device_add(&ulpi->dev);
if (ret)
return ret;
}
root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root);
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
@@ -334,9 +330,10 @@ struct ulpi *ulpi_register_interface(struct device *dev,
ulpi->ops = ops;
ret = ulpi_register(dev, ulpi);
if (ret)
if (ret) {
put_device(&ulpi->dev);
return ERR_PTR(ret);
}
return ulpi;
}
+1 -1
View File
@@ -3148,7 +3148,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
delay = HUB_LONG_RESET_TIME;
}
dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n");
dev_err_ratelimited(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n");
done:
if (status == 0) {
+6
View File
@@ -296,6 +296,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* CarrolTouch 4500U */
{ USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME },
/* Samsung T5 EVO Portable SSD */
{ USB_DEVICE(0x04e8, 0x6200), .driver_info = USB_QUIRK_NO_LPM },
/* Samsung Android phone modem - ID conflict with SPH-I500 */
{ USB_DEVICE(0x04e8, 0x6601), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
@@ -576,6 +579,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* VLI disk */
{ USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM },
/* VIA Labs, Inc. USB2.0 Hub */
{ USB_DEVICE(0x2109, 0x2817), .driver_info = USB_QUIRK_NO_LPM },
/* Raydium Touchscreen */
{ USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM },
+2 -2
View File
@@ -789,9 +789,9 @@ static void dwc3_ulpi_setup(struct dwc3 *dwc)
if (dwc->enable_usb2_transceiver_delay) {
for (index = 0; index < dwc->num_usb2_ports; index++) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index));
reg |= DWC3_GUSB2PHYCFG_XCVRDLY;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
}
}
}
+9 -5
View File
@@ -907,35 +907,39 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
ret = priv->drvdata->usb_init(priv);
if (ret)
return ret;
goto err_rearm;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
ret = phy_init(priv->phys[i]);
if (ret)
return ret;
goto err_rearm;
}
/* Set PHY Power */
for (i = 0 ; i < PHY_COUNT ; ++i) {
ret = phy_power_on(priv->phys[i]);
if (ret)
return ret;
goto err_rearm;
}
if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
ret = regulator_enable(priv->vbus);
if (ret)
return ret;
goto err_rearm;
}
if (priv->drvdata->usb_post_init) {
ret = priv->drvdata->usb_post_init(priv);
if (ret)
return ret;
goto err_rearm;
}
return 0;
err_rearm:
reset_control_rearm(priv->reset);
return ret;
}
static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
+36 -7
View File
@@ -3934,15 +3934,48 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
}
}
static bool dwc3_prepare_disconnect_gadget(struct dwc3 *dwc,
struct usb_gadget_driver **driver,
struct usb_gadget **gadget)
{
if (!dwc->async_callbacks || !dwc->gadget_driver ||
!dwc->gadget_driver->disconnect)
return false;
*driver = dwc->gadget_driver;
*gadget = dwc->gadget;
return true;
}
static void dwc3_disconnect_gadget(struct dwc3 *dwc)
{
if (dwc->async_callbacks && dwc->gadget_driver->disconnect) {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
if (dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) {
spin_unlock(&dwc->lock);
dwc->gadget_driver->disconnect(dwc->gadget);
driver->disconnect(gadget);
spin_lock(&dwc->lock);
}
}
static void dwc3_disconnect_gadget_sleepable(struct dwc3 *dwc)
{
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
if (!dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) {
spin_unlock_irqrestore(&dwc->lock, flags);
return;
}
spin_unlock_irqrestore(&dwc->lock, flags);
driver->disconnect(gadget);
}
static void dwc3_suspend_gadget(struct dwc3 *dwc)
{
if (dwc->async_callbacks && dwc->gadget_driver->suspend) {
@@ -4838,7 +4871,6 @@ EXPORT_SYMBOL_GPL(dwc3_gadget_exit);
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
unsigned long flags;
int ret;
ret = dwc3_gadget_soft_disconnect(dwc);
@@ -4852,10 +4884,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
return -EAGAIN;
}
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->gadget_driver)
dwc3_disconnect_gadget(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
dwc3_disconnect_gadget_sleepable(dwc);
return 0;
}
+4 -2
View File
@@ -4267,8 +4267,6 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb,
return 0;
fail:
iso_sched_free(stream, sched);
urb->hcpriv = NULL;
return status;
}
@@ -4562,6 +4560,10 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb,
else
usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb);
done_not_linked:
if (status < 0) {
iso_sched_free(stream, urb->hcpriv);
urb->hcpriv = NULL;
}
spin_unlock_irqrestore(&fotg210->lock, flags);
done:
return status;
+3 -2
View File
@@ -1863,9 +1863,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (cdev->config)
config = cdev->config;
else
config = list_first_entry(
config = list_first_entry_or_null(
&cdev->configs,
struct usb_configuration, list);
struct usb_configuration,
list);
if (!config)
goto done;
+5 -4
View File
@@ -288,6 +288,7 @@ static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data);
static void ffs_release_dev(struct ffs_dev *ffs_dev);
static int ffs_ready(struct ffs_data *ffs);
static void ffs_closed(struct ffs_data *ffs);
static void ffs_reset_work(struct work_struct *work);
/* Misc helper functions ****************************************************/
@@ -1374,7 +1375,6 @@ ffs_epfile_release(struct inode *inode, struct file *file)
mutex_unlock(&epfile->dmabufs_mutex);
__ffs_epfile_read_buffer_free(epfile);
ffs_data_closed(epfile->ffs);
return 0;
@@ -1704,6 +1704,7 @@ static int ffs_dmabuf_transfer(struct file *file,
resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE;
dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir);
dma_fence_put(&fence->base);
dma_resv_unlock(dmabuf->resv);
/* Now that the dma_fence is in place, queue the transfer. */
@@ -2221,6 +2222,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name)
init_waitqueue_head(&ffs->ev.waitq);
init_waitqueue_head(&ffs->wait);
init_completion(&ffs->ep0req_completion);
INIT_WORK(&ffs->reset_work, ffs_reset_work);
/* XXX REVISIT need to update it in some places, or do we? */
ffs->ev.can_stall = 1;
@@ -2364,6 +2366,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
else
sprintf(epfile->name, "ep%u", i);
epfile->in = (ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
err = ffs_sb_create_file(ffs->sb, epfile->name,
epfile, &ffs_epfile_operations);
if (err) {
@@ -2389,6 +2392,7 @@ static void ffs_epfiles_destroy(struct super_block *sb,
for (; count; --count, ++epfile) {
BUG_ON(mutex_is_locked(&epfile->mutex));
__ffs_epfile_read_buffer_free(epfile);
simple_remove_by_name(root, epfile->name, clear_one);
}
@@ -2453,7 +2457,6 @@ static int ffs_func_eps_enable(struct ffs_function *func)
ret = usb_ep_enable(ep->ep);
if (!ret) {
epfile->ep = ep;
epfile->in = usb_endpoint_dir_in(ep->ep->desc);
epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc);
} else {
break;
@@ -3775,7 +3778,6 @@ static int ffs_func_set_alt(struct usb_function *f,
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
spin_unlock_irqrestore(&ffs->eps_lock, flags);
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return -ENODEV;
}
@@ -3806,7 +3808,6 @@ static void ffs_func_disable(struct usb_function *f)
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
spin_unlock_irqrestore(&ffs->eps_lock, flags);
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return;
}
+1 -2
View File
@@ -363,12 +363,11 @@ printer_open(struct inode *inode, struct file *fd)
ret = 0;
/* Change the printer status to show that it's on-line. */
dev->printer_status |= PRINTER_SELECTED;
kref_get(&dev->kref);
}
spin_unlock_irqrestore(&dev->lock, flags);
kref_get(&dev->kref);
return ret;
}
+16 -4
View File
@@ -591,6 +591,7 @@ static int rndis_init_response(struct rndis_params *params,
static int rndis_query_response(struct rndis_params *params,
rndis_query_msg_type *buf)
{
u32 BufLength, BufOffset;
rndis_query_cmplt_type *resp;
rndis_resp_t *r;
@@ -598,6 +599,13 @@ static int rndis_query_response(struct rndis_params *params,
if (!params->dev)
return -ENOTSUPP;
BufLength = le32_to_cpu(buf->InformationBufferLength);
BufOffset = le32_to_cpu(buf->InformationBufferOffset);
if ((BufLength > RNDIS_MAX_TOTAL_SIZE) ||
(BufOffset > RNDIS_MAX_TOTAL_SIZE) ||
(BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE))
return -EINVAL;
/*
* we need more memory:
* gen_ndis_query_resp expects enough space for
@@ -614,10 +622,8 @@ static int rndis_query_response(struct rndis_params *params,
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID),
le32_to_cpu(buf->InformationBufferOffset)
+ 8 + (u8 *)buf,
le32_to_cpu(buf->InformationBufferLength),
r)) {
BufOffset + 8 + (u8 *)buf,
BufLength, r)) {
/* OID not supported */
resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
resp->MessageLength = cpu_to_le32(sizeof *resp);
@@ -1074,6 +1080,12 @@ int rndis_rm_hdr(struct gether *port,
/* tmp points to a struct rndis_packet_msg_type */
__le32 *tmp = (void *)skb->data;
/* Need at least MessageType, MessageLength, DataOffset, DataLength */
if (skb->len < 16) {
dev_kfree_skb_any(skb);
return -EINVAL;
}
/* MessageType, MessageLength */
if (cpu_to_le32(RNDIS_MSG_PACKET)
!= get_unaligned(tmp++)) {
+28 -1
View File
@@ -31,8 +31,9 @@ static const struct bus_type gadget_bus_type;
/**
* struct usb_udc - describes one usb device controller
* @driver: the gadget driver pointer. For use by the class code
* @dev: the child device to the actual controller
* @gadget: the gadget. For use by the class code
* @gadget_release: the gadget's release routine
* @dev: the child device to the actual controller
* @list: for use by the udc class driver
* @vbus: for udcs who care about vbus status, this value is real vbus status;
* for udcs who do not care about vbus status, this value is always true
@@ -53,6 +54,7 @@ static const struct bus_type gadget_bus_type;
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
void (*gadget_release)(struct device *dev);
struct device dev;
struct list_head list;
bool vbus;
@@ -1362,6 +1364,17 @@ static void usb_udc_nop_release(struct device *dev)
dev_vdbg(dev, "%s\n", __func__);
}
static void usb_gadget_release(struct device *dev)
{
struct usb_gadget *gadget = dev_to_usb_gadget(dev);
struct usb_udc *udc = gadget->udc;
/* Cache the gadget's release routine to prevent UAF */
void (*release)(struct device *dev) = udc->gadget_release;
put_device(&udc->dev);
release(dev);
}
/**
* usb_initialize_gadget - initialize a gadget and its embedded struct device
* @parent: the parent device to this udc. Usually the controller driver's
@@ -1418,6 +1431,14 @@ int usb_add_gadget(struct usb_gadget *gadget)
mutex_init(&udc->connect_lock);
udc->started = false;
/*
* Align decoupled lifecycles: take a UDC reference to ensure it
* remains allocated until the gadget is released, requiring an
* override of the gadget's release routine to drop it.
*/
udc->gadget_release = gadget->dev.release;
gadget->dev.release = usb_gadget_release;
get_device(&udc->dev);
mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list);
@@ -1462,6 +1483,12 @@ int usb_add_gadget(struct usb_gadget *gadget)
mutex_lock(&udc_lock);
list_del(&udc->list);
mutex_unlock(&udc_lock);
/*
* Revert the override and drop the UDC reference to prevent
* leaking the UDC if the gadget was statically allocated.
*/
gadget->dev.release = udc->gadget_release;
put_device(&udc->dev);
err_put_udc:
put_device(&udc->dev);
+9 -2
View File
@@ -1623,6 +1623,7 @@ iso_stream_schedule(
status = 1; /* and give it back immediately */
iso_sched_free(stream, sched);
sched = NULL;
urb->hcpriv = NULL;
}
}
urb->error_count = skip / period;
@@ -1653,8 +1654,6 @@ iso_stream_schedule(
return status;
fail:
iso_sched_free(stream, sched);
urb->hcpriv = NULL;
return status;
}
@@ -1966,6 +1965,10 @@ static int itd_submit(struct ehci_hcd *ehci, struct urb *urb,
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
}
done_not_linked:
if (status < 0) {
iso_sched_free(stream, urb->hcpriv);
urb->hcpriv = NULL;
}
spin_unlock_irqrestore(&ehci->lock, flags);
done:
return status;
@@ -2343,6 +2346,10 @@ static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb,
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
}
done_not_linked:
if (status < 0) {
iso_sched_free(stream, urb->hcpriv);
urb->hcpriv = NULL;
}
spin_unlock_irqrestore(&ehci->lock, flags);
done:
return status;
+1
View File
@@ -1591,6 +1591,7 @@ sl811h_remove(struct platform_device *dev)
remove_debug_file(sl811);
usb_remove_hcd(hcd);
device_wakeup_disable(hcd->self.controller);
/* some platforms may use IORESOURCE_IO */
res = platform_get_resource(dev, IORESOURCE_MEM, 1);
+58 -2
View File
@@ -646,6 +646,31 @@ static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable)
static void xhci_dbc_set_state(struct xhci_dbc *dbc, enum dbc_state new_state)
{
if (dbc->state == new_state)
return;
switch (new_state) {
case DS_ENABLED:
/*
* DbC pm usage is 1 here, both when moved from disconnect or
* configured states, or when setting initial DbC enable state.
* Just enable pending put
*/
dev_dbg(dbc->dev, "DbC set pending_rpm_put = 1\n");
dbc->pending_rpm_put = 1;
break;
case DS_CONNECTED:
if (dbc->pending_rpm_put)
/* DbC pm usage still 1, just remove pending put */
dbc->pending_rpm_put = 0;
else
/* DbC pm usage was put to 0, call get */
pm_runtime_get(dbc->dev);
break;
default:
break;
}
dbc->state_timestamp = jiffies;
dbc->state = new_state;
}
@@ -681,7 +706,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
WARN_ON(!dbc);
pm_runtime_get_sync(dbc->dev); /* note this was self.controller */
pm_runtime_get(dbc->dev);
spin_lock_irqsave(&dbc->lock, flags);
ret = xhci_do_dbc_start(dbc);
@@ -706,6 +731,7 @@ err_unlock:
static void xhci_dbc_stop(struct xhci_dbc *dbc)
{
unsigned long flags;
bool need_rpm_put = false;
WARN_ON(!dbc);
@@ -731,12 +757,20 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
spin_lock_irqsave(&dbc->lock, flags);
writel(0, &dbc->regs->control);
if (dbc->state == DS_CONNECTED || dbc->state == DS_CONFIGURED ||
dbc->pending_rpm_put)
need_rpm_put = true;
dbc->pending_rpm_put = 0;
xhci_dbc_set_state(dbc, DS_DISABLED);
spin_unlock_irqrestore(&dbc->lock, flags);
xhci_dbc_mem_cleanup(dbc);
pm_runtime_put(dbc->dev); /* note, was self.controller */
if (need_rpm_put)
pm_runtime_put(dbc->dev);
}
static void
@@ -908,6 +942,12 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
dev_info(dbc->dev, "DbC connected\n");
} else if (!(ctrl & DBC_CTRL_DBC_ENABLE)) {
dev_err(dbc->dev, "unexpected DbC disable, xHC reset?\n");
} else if (dbc->pending_rpm_put &&
time_is_before_jiffies(dbc->state_timestamp +
msecs_to_jiffies(DBC_AUTOSUSPEND_DELAY))) {
dbc->pending_rpm_put = 0;
dev_dbg(dbc->dev, "DbC Enabled state for 15 seconds, allow rpm suspend\n");
pm_runtime_put(dbc->dev);
}
return EVT_DONE;
@@ -1096,6 +1136,9 @@ static ssize_t dbc_show(struct device *dev,
if (dbc->state >= ARRAY_SIZE(dbc_state_strings))
return sysfs_emit(buf, "unknown\n");
if (dbc->resume_required)
return sysfs_emit(buf, "suspended\n");
return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]);
}
@@ -1110,12 +1153,25 @@ static ssize_t dbc_store(struct device *dev,
dbc = xhci->dbc;
if (sysfs_streq(buf, "enable")) {
pm_runtime_get_sync(dbc->dev);
mutex_lock(&dbc->enable_mutex);
/*
* DbC may already be enabled here if xhci was suspended with
* dbc->resume_required set, and resumed by pm_runtime_get_sync()
* above. In this case we end up calling xhci_dbc_start() twice,
* second time returns an error but is harmless
*/
xhci_dbc_start(dbc);
mutex_unlock(&dbc->enable_mutex);
pm_runtime_put(dbc->dev);
} else if (sysfs_streq(buf, "disable")) {
mutex_lock(&dbc->enable_mutex);
dbc->resume_required = 0;
xhci_dbc_stop(dbc);
mutex_unlock(&dbc->enable_mutex);
} else {
return -EINVAL;
Loaded 20 of 53 files, more files were not shown because too many files have changed in this diff. Show more