Skip to content

Commit c99b38c

Browse files
matnymangregkh
authored andcommitted
xhci: add support to allocate several interrupters
Modify the XHCI drivers to accommodate for handling multiple event rings in case there are multiple interrupters. Add the required APIs so clients are able to allocate/request for an interrupter ring, and pass this information back to the client driver. This allows for users to handle the resource accordingly, such as passing the event ring base address to an audio DSP. There is no actual support for multiple MSI/MSI-X vectors. [export xhci_initialize_ring_info() -wcheng] Signed-off-by: Mathias Nyman <[email protected]> Signed-off-by: Wesley Cheng <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 49a78b0 commit c99b38c

File tree

5 files changed

+137
-32
lines changed

5 files changed

+137
-32
lines changed

drivers/usb/host/xhci-debugfs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
693693
"command-ring",
694694
xhci->debugfs_root);
695695

696-
xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring,
696+
xhci_debugfs_create_ring_dir(xhci, &xhci->interrupters[0]->event_ring,
697697
"event-ring",
698698
xhci->debugfs_root);
699699

drivers/usb/host/xhci-mem.c

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring,
323323
*/
324324
ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
325325
}
326+
EXPORT_SYMBOL_GPL(xhci_initialize_ring_info);
326327

327328
/* Allocate segments and link them for a ring */
328329
static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
@@ -1855,17 +1856,46 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
18551856
kfree(ir);
18561857
}
18571858

1859+
void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir)
1860+
{
1861+
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
1862+
unsigned int intr_num;
1863+
1864+
/* interrupter 0 is primary interrupter, don't touch it */
1865+
if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters)
1866+
xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
1867+
1868+
/* fixme, should we check xhci->interrupter[intr_num] == ir */
1869+
/* fixme locking */
1870+
1871+
spin_lock_irq(&xhci->lock);
1872+
1873+
intr_num = ir->intr_num;
1874+
1875+
xhci_remove_interrupter(xhci, ir);
1876+
xhci->interrupters[intr_num] = NULL;
1877+
1878+
spin_unlock_irq(&xhci->lock);
1879+
1880+
xhci_free_interrupter(xhci, ir);
1881+
}
1882+
EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter);
1883+
18581884
void xhci_mem_cleanup(struct xhci_hcd *xhci)
18591885
{
18601886
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
18611887
int i, j, num_ports;
18621888

18631889
cancel_delayed_work_sync(&xhci->cmd_timer);
18641890

1865-
xhci_remove_interrupter(xhci, xhci->interrupter);
1866-
xhci_free_interrupter(xhci, xhci->interrupter);
1867-
xhci->interrupter = NULL;
1868-
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring");
1891+
for (i = 0; i < xhci->max_interrupters; i++) {
1892+
if (xhci->interrupters[i]) {
1893+
xhci_remove_interrupter(xhci, xhci->interrupters[i]);
1894+
xhci_free_interrupter(xhci, xhci->interrupters[i]);
1895+
xhci->interrupters[i] = NULL;
1896+
}
1897+
}
1898+
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed interrupters");
18691899

18701900
if (xhci->cmd_ring)
18711901
xhci_ring_free(xhci, xhci->cmd_ring);
@@ -1935,6 +1965,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
19351965
for (i = 0; i < xhci->num_port_caps; i++)
19361966
kfree(xhci->port_caps[i].psi);
19371967
kfree(xhci->port_caps);
1968+
kfree(xhci->interrupters);
19381969
xhci->num_port_caps = 0;
19391970

19401971
xhci->usb2_rhub.ports = NULL;
@@ -1943,6 +1974,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
19431974
xhci->rh_bw = NULL;
19441975
xhci->ext_caps = NULL;
19451976
xhci->port_caps = NULL;
1977+
xhci->interrupters = NULL;
19461978

19471979
xhci->page_size = 0;
19481980
xhci->page_shift = 0;
@@ -2248,18 +2280,20 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
22482280
}
22492281

22502282
static struct xhci_interrupter *
2251-
xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags)
2283+
xhci_alloc_interrupter(struct xhci_hcd *xhci, int segs, gfp_t flags)
22522284
{
22532285
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
22542286
struct xhci_interrupter *ir;
2255-
unsigned int num_segs;
2287+
unsigned int num_segs = segs;
22562288
int ret;
22572289

22582290
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
22592291
if (!ir)
22602292
return NULL;
22612293

2262-
num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2),
2294+
/* number of ring segments should be greater than 0 */
2295+
if (segs <= 0)
2296+
num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2),
22632297
ERST_MAX_SEGS);
22642298

22652299
ir->event_ring = xhci_ring_alloc(xhci, num_segs, 1, TYPE_EVENT, 0,
@@ -2294,6 +2328,13 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
22942328
return -EINVAL;
22952329
}
22962330

2331+
if (xhci->interrupters[intr_num]) {
2332+
xhci_warn(xhci, "Interrupter %d\n already set up", intr_num);
2333+
return -EINVAL;
2334+
}
2335+
2336+
xhci->interrupters[intr_num] = ir;
2337+
ir->intr_num = intr_num;
22972338
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
22982339

22992340
/* set ERST count with the number of entries in the segment table */
@@ -2313,10 +2354,52 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
23132354
return 0;
23142355
}
23152356

2357+
struct xhci_interrupter *
2358+
xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg)
2359+
{
2360+
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
2361+
struct xhci_interrupter *ir;
2362+
unsigned int i;
2363+
int err = -ENOSPC;
2364+
2365+
if (!xhci->interrupters || xhci->max_interrupters <= 1)
2366+
return NULL;
2367+
2368+
ir = xhci_alloc_interrupter(xhci, num_seg, GFP_KERNEL);
2369+
if (!ir)
2370+
return NULL;
2371+
2372+
spin_lock_irq(&xhci->lock);
2373+
2374+
/* Find available secondary interrupter, interrupter 0 is reserved for primary */
2375+
for (i = 1; i < xhci->max_interrupters; i++) {
2376+
if (xhci->interrupters[i] == NULL) {
2377+
err = xhci_add_interrupter(xhci, ir, i);
2378+
break;
2379+
}
2380+
}
2381+
2382+
spin_unlock_irq(&xhci->lock);
2383+
2384+
if (err) {
2385+
xhci_warn(xhci, "Failed to add secondary interrupter, max interrupters %d\n",
2386+
xhci->max_interrupters);
2387+
xhci_free_interrupter(xhci, ir);
2388+
return NULL;
2389+
}
2390+
2391+
xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
2392+
i, xhci->max_interrupters);
2393+
2394+
return ir;
2395+
}
2396+
EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
2397+
23162398
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
23172399
{
2318-
dma_addr_t dma;
2400+
struct xhci_interrupter *ir;
23192401
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
2402+
dma_addr_t dma;
23202403
unsigned int val, val2;
23212404
u64 val_64;
23222405
u32 page_size, temp;
@@ -2440,11 +2523,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
24402523
/* Allocate and set up primary interrupter 0 with an event ring. */
24412524
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
24422525
"Allocating primary event ring");
2443-
xhci->interrupter = xhci_alloc_interrupter(xhci, flags);
2444-
if (!xhci->interrupter)
2526+
xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters),
2527+
flags, dev_to_node(dev));
2528+
2529+
ir = xhci_alloc_interrupter(xhci, 0, flags);
2530+
if (!ir)
24452531
goto fail;
24462532

2447-
if (xhci_add_interrupter(xhci, xhci->interrupter, 0))
2533+
if (xhci_add_interrupter(xhci, ir, 0))
24482534
goto fail;
24492535

24502536
xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;

drivers/usb/host/xhci-ring.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3061,7 +3061,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
30613061
writel(status, &xhci->op_regs->status);
30623062

30633063
/* This is the handler of the primary interrupter */
3064-
ir = xhci->interrupter;
3064+
ir = xhci->interrupters[0];
30653065
if (!hcd->msi_enabled) {
30663066
u32 irq_pending;
30673067
irq_pending = readl(&ir->ir_set->irq_pending);

drivers/usb/host/xhci.c

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ static int xhci_init(struct usb_hcd *hcd)
480480

481481
static int xhci_run_finished(struct xhci_hcd *xhci)
482482
{
483-
struct xhci_interrupter *ir = xhci->interrupter;
483+
struct xhci_interrupter *ir = xhci->interrupters[0];
484484
unsigned long flags;
485485
u32 temp;
486486

@@ -532,7 +532,7 @@ int xhci_run(struct usb_hcd *hcd)
532532
u64 temp_64;
533533
int ret;
534534
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
535-
struct xhci_interrupter *ir = xhci->interrupter;
535+
struct xhci_interrupter *ir = xhci->interrupters[0];
536536
/* Start the xHCI host controller running only after the USB 2.0 roothub
537537
* is setup.
538538
*/
@@ -596,7 +596,7 @@ void xhci_stop(struct usb_hcd *hcd)
596596
{
597597
u32 temp;
598598
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
599-
struct xhci_interrupter *ir = xhci->interrupter;
599+
struct xhci_interrupter *ir = xhci->interrupters[0];
600600

601601
mutex_lock(&xhci->mutex);
602602

@@ -692,36 +692,51 @@ EXPORT_SYMBOL_GPL(xhci_shutdown);
692692
#ifdef CONFIG_PM
693693
static void xhci_save_registers(struct xhci_hcd *xhci)
694694
{
695-
struct xhci_interrupter *ir = xhci->interrupter;
695+
struct xhci_interrupter *ir;
696+
unsigned int i;
696697

697698
xhci->s3.command = readl(&xhci->op_regs->command);
698699
xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification);
699700
xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
700701
xhci->s3.config_reg = readl(&xhci->op_regs->config_reg);
701702

702-
if (!ir)
703-
return;
703+
/* save both primary and all secondary interrupters */
704+
/* fixme, shold we lock to prevent race with remove secondary interrupter? */
705+
for (i = 0; i < xhci->max_interrupters; i++) {
706+
ir = xhci->interrupters[i];
707+
if (!ir)
708+
continue;
704709

705-
ir->s3_erst_size = readl(&ir->ir_set->erst_size);
706-
ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
707-
ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
708-
ir->s3_irq_pending = readl(&ir->ir_set->irq_pending);
709-
ir->s3_irq_control = readl(&ir->ir_set->irq_control);
710+
ir->s3_erst_size = readl(&ir->ir_set->erst_size);
711+
ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
712+
ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
713+
ir->s3_irq_pending = readl(&ir->ir_set->irq_pending);
714+
ir->s3_irq_control = readl(&ir->ir_set->irq_control);
715+
}
710716
}
711717

712718
static void xhci_restore_registers(struct xhci_hcd *xhci)
713719
{
714-
struct xhci_interrupter *ir = xhci->interrupter;
720+
struct xhci_interrupter *ir;
721+
unsigned int i;
715722

716723
writel(xhci->s3.command, &xhci->op_regs->command);
717724
writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification);
718725
xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr);
719726
writel(xhci->s3.config_reg, &xhci->op_regs->config_reg);
720-
writel(ir->s3_erst_size, &ir->ir_set->erst_size);
721-
xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base);
722-
xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue);
723-
writel(ir->s3_irq_pending, &ir->ir_set->irq_pending);
724-
writel(ir->s3_irq_control, &ir->ir_set->irq_control);
727+
728+
/* FIXME should we lock to protect against freeing of interrupters */
729+
for (i = 0; i < xhci->max_interrupters; i++) {
730+
ir = xhci->interrupters[i];
731+
if (!ir)
732+
continue;
733+
734+
writel(ir->s3_erst_size, &ir->ir_set->erst_size);
735+
xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base);
736+
xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue);
737+
writel(ir->s3_irq_pending, &ir->ir_set->irq_pending);
738+
writel(ir->s3_irq_control, &ir->ir_set->irq_control);
739+
}
725740
}
726741

727742
static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
@@ -1084,7 +1099,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
10841099
xhci_dbg(xhci, "// Disabling event ring interrupts\n");
10851100
temp = readl(&xhci->op_regs->status);
10861101
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
1087-
xhci_disable_interrupter(xhci->interrupter);
1102+
xhci_disable_interrupter(xhci->interrupters[0]);
10881103

10891104
xhci_dbg(xhci, "cleaning up memory\n");
10901105
xhci_mem_cleanup(xhci);

drivers/usb/host/xhci.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,7 @@ struct xhci_hcd {
17741774
struct reset_control *reset;
17751775
/* data structures */
17761776
struct xhci_device_context_array *dcbaa;
1777-
struct xhci_interrupter *interrupter;
1777+
struct xhci_interrupter **interrupters;
17781778
struct xhci_ring *cmd_ring;
17791779
unsigned int cmd_ring_state;
17801780
#define CMD_RING_STATE_RUNNING (1 << 0)
@@ -2085,6 +2085,10 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
20852085
int type, gfp_t flags);
20862086
void xhci_free_container_ctx(struct xhci_hcd *xhci,
20872087
struct xhci_container_ctx *ctx);
2088+
struct xhci_interrupter *
2089+
xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg);
2090+
void xhci_remove_secondary_interrupter(struct usb_hcd
2091+
*hcd, struct xhci_interrupter *ir);
20882092

20892093
/* xHCI host controller glue */
20902094
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);

0 commit comments

Comments
 (0)