xhci: dbc: poll at different rate depending on data transfer activity
authorMathias Nyman <mathias.nyman@linux.intel.com>
Thu, 29 Feb 2024 14:14:37 +0000 (16:14 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 2 Mar 2024 19:25:28 +0000 (20:25 +0100)
DbC driver starts polling for events immediately when DbC is enabled.
The current polling interval is 1ms, which keeps the CPU busy, impacting
power management even when there are no active data transfers.

Solve this by polling at a slower rate, with a 64ms interval as default
until a transfer request is queued, or if there are still are pending
unhandled transfers at event completion.

Tested-by: Uday M Bhat <uday.m.bhat@intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20240229141438.619372-9-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci-dbgcap.c
drivers/usb/host/xhci-dbgcap.h

index d82935d31126dd020d941b7a7cd8c78a93f3392f..8a9869ef0db66a5df132a71c66f19f5fdcee14c4 100644 (file)
@@ -634,7 +634,8 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
                return ret;
        }
 
-       return mod_delayed_work(system_wq, &dbc->event_work, 1);
+       return mod_delayed_work(system_wq, &dbc->event_work,
+                               msecs_to_jiffies(dbc->poll_interval));
 }
 
 static void xhci_dbc_stop(struct xhci_dbc *dbc)
@@ -899,8 +900,10 @@ static void xhci_dbc_handle_events(struct work_struct *work)
        enum evtreturn          evtr;
        struct xhci_dbc         *dbc;
        unsigned long           flags;
+       unsigned int            poll_interval;
 
        dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work);
+       poll_interval = dbc->poll_interval;
 
        spin_lock_irqsave(&dbc->lock, flags);
        evtr = xhci_dbc_do_handle_events(dbc);
@@ -916,13 +919,18 @@ static void xhci_dbc_handle_events(struct work_struct *work)
                        dbc->driver->disconnect(dbc);
                break;
        case EVT_DONE:
+               /* set fast poll rate if there are pending data transfers */
+               if (!list_empty(&dbc->eps[BULK_OUT].list_pending) ||
+                   !list_empty(&dbc->eps[BULK_IN].list_pending))
+                       poll_interval = 1;
                break;
        default:
                dev_info(dbc->dev, "stop handling dbc events\n");
                return;
        }
 
-       mod_delayed_work(system_wq, &dbc->event_work, 1);
+       mod_delayed_work(system_wq, &dbc->event_work,
+                        msecs_to_jiffies(poll_interval));
 }
 
 static const char * const dbc_state_strings[DS_MAX] = {
@@ -1175,6 +1183,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
        dbc->idVendor = DBC_VENDOR_ID;
        dbc->bcdDevice = DBC_DEVICE_REV;
        dbc->bInterfaceProtocol = DBC_PROTOCOL;
+       dbc->poll_interval = DBC_POLL_INTERVAL_DEFAULT;
 
        if (readl(&dbc->regs->control) & DBC_CTRL_DBC_ENABLE)
                goto err;
index e39e3ae1677aeb48bfc00ad8f32d9157901b1dc1..92661b555c2a29db77b78ac75f593f71fe8cc689 100644 (file)
@@ -94,6 +94,7 @@ struct dbc_ep {
 
 #define DBC_QUEUE_SIZE                 16
 #define DBC_WRITE_BUF_SIZE             8192
+#define DBC_POLL_INTERVAL_DEFAULT      64      /* milliseconds */
 
 /*
  * Private structure for DbC hardware state:
@@ -140,6 +141,7 @@ struct xhci_dbc {
 
        enum dbc_state                  state;
        struct delayed_work             event_work;
+       unsigned int                    poll_interval;  /* ms */
        unsigned                        resume_required:1;
        struct dbc_ep                   eps[2];