ANDROID: GKI: usb: xhci: Add support for secondary interrupters
Implement APIs to dynamically allocate and free secondary event rings based upon interrupter number. Also add exported APIs in usb core layer which allows secondary event ring management via remote processor entity. Change-Id: I5ee7d44d6cad8e35e22d3c1a027a1eec5d208585 Signed-off-by: Hemant Kumar <hemantk@codeaurora.org> Signed-off-by: Jack Pham <jackp@codeaurora.org> Signed-off-by: Mayank Rana <mrana@codeaurora.org> (cherry picked from commit8d48fb820f) [connoro: squashed the following commits:a44f0d73afusb: xhci: Clear event handler busy flag upon event ring cleanupa1504b40dausb: xhci: Acknowledge pending events in secondary event ringa7e7dd8a34usb: host: xhci: Fix bound check for interrupter numberc4d9817a3cusb: core: Allow secondary event ring clean upon disconnect576c1e1fe6usb: host: xhci: Fix secondary event ring setup] Bug: 151258428 Signed-off-by: Connor O'Brien <connoro@google.com>
This commit is contained in:
committed by
Connor O'Brien
parent
a6c834c026
commit
5dfdaa15ca
@@ -2233,6 +2233,28 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
|
||||
return hcd->driver->get_frame_number (hcd);
|
||||
}
|
||||
|
||||
int usb_hcd_sec_event_ring_setup(struct usb_device *udev,
|
||||
unsigned int intr_num)
|
||||
{
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
if (!HCD_RH_RUNNING(hcd))
|
||||
return 0;
|
||||
|
||||
return hcd->driver->sec_event_ring_setup(hcd, intr_num);
|
||||
}
|
||||
|
||||
int usb_hcd_sec_event_ring_cleanup(struct usb_device *udev,
|
||||
unsigned int intr_num)
|
||||
{
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
if (!HCD_RH_RUNNING(hcd))
|
||||
return 0;
|
||||
|
||||
return hcd->driver->sec_event_ring_cleanup(hcd, intr_num);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
int usb_hcd_get_controller_id(struct usb_device *udev)
|
||||
|
||||
@@ -825,6 +825,23 @@ int usb_get_current_frame_number(struct usb_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_current_frame_number);
|
||||
|
||||
int usb_sec_event_ring_setup(struct usb_device *dev,
|
||||
unsigned int intr_num)
|
||||
{
|
||||
if (dev->state == USB_STATE_NOTATTACHED)
|
||||
return 0;
|
||||
|
||||
return usb_hcd_sec_event_ring_setup(dev, intr_num);
|
||||
}
|
||||
EXPORT_SYMBOL(usb_sec_event_ring_setup);
|
||||
|
||||
int usb_sec_event_ring_cleanup(struct usb_device *dev,
|
||||
unsigned int intr_num)
|
||||
{
|
||||
return usb_hcd_sec_event_ring_cleanup(dev, intr_num);
|
||||
}
|
||||
EXPORT_SYMBOL(usb_sec_event_ring_cleanup);
|
||||
|
||||
/**
|
||||
* usb_get_controller_id - returns the host controller id.
|
||||
* @dev: the device whose host controller id is being queried.
|
||||
|
||||
@@ -1837,6 +1837,137 @@ void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
|
||||
erst->entries = NULL;
|
||||
}
|
||||
|
||||
void xhci_handle_sec_intr_events(struct xhci_hcd *xhci, int intr_num)
|
||||
{
|
||||
union xhci_trb *erdp_trb, *current_trb;
|
||||
struct xhci_segment *seg;
|
||||
u64 erdp_reg;
|
||||
u32 iman_reg;
|
||||
dma_addr_t deq;
|
||||
unsigned long segment_offset;
|
||||
|
||||
/* disable irq, ack pending interrupt and ack all pending events */
|
||||
|
||||
iman_reg =
|
||||
readl_relaxed(&xhci->sec_ir_set[intr_num]->irq_pending);
|
||||
iman_reg &= ~IMAN_IE;
|
||||
writel_relaxed(iman_reg,
|
||||
&xhci->sec_ir_set[intr_num]->irq_pending);
|
||||
iman_reg =
|
||||
readl_relaxed(&xhci->sec_ir_set[intr_num]->irq_pending);
|
||||
if (iman_reg & IMAN_IP)
|
||||
writel_relaxed(iman_reg,
|
||||
&xhci->sec_ir_set[intr_num]->irq_pending);
|
||||
|
||||
/* last acked event trb is in erdp reg */
|
||||
erdp_reg =
|
||||
xhci_read_64(xhci, &xhci->sec_ir_set[intr_num]->erst_dequeue);
|
||||
deq = (dma_addr_t)(erdp_reg & ~ERST_PTR_MASK);
|
||||
if (!deq) {
|
||||
pr_debug("%s: event ring handling not required\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
seg = xhci->sec_event_ring[intr_num]->first_seg;
|
||||
segment_offset = deq - seg->dma;
|
||||
|
||||
/* find out virtual address of the last acked event trb */
|
||||
erdp_trb = current_trb = &seg->trbs[0] +
|
||||
(segment_offset/sizeof(*current_trb));
|
||||
|
||||
/* read cycle state of the last acked trb to find out CCS */
|
||||
xhci->sec_event_ring[intr_num]->cycle_state =
|
||||
(current_trb->event_cmd.flags & TRB_CYCLE);
|
||||
|
||||
while (1) {
|
||||
/* last trb of the event ring: toggle cycle state */
|
||||
if (current_trb == &seg->trbs[TRBS_PER_SEGMENT - 1]) {
|
||||
xhci->sec_event_ring[intr_num]->cycle_state ^= 1;
|
||||
current_trb = &seg->trbs[0];
|
||||
} else {
|
||||
current_trb++;
|
||||
}
|
||||
|
||||
/* cycle state transition */
|
||||
if ((le32_to_cpu(current_trb->event_cmd.flags) & TRB_CYCLE) !=
|
||||
xhci->sec_event_ring[intr_num]->cycle_state)
|
||||
break;
|
||||
}
|
||||
|
||||
if (erdp_trb != current_trb) {
|
||||
deq =
|
||||
xhci_trb_virt_to_dma(xhci->sec_event_ring[intr_num]->deq_seg,
|
||||
current_trb);
|
||||
if (deq == 0)
|
||||
xhci_warn(xhci,
|
||||
"WARN invalid SW event ring dequeue ptr.\n");
|
||||
/* Update HC event ring dequeue pointer */
|
||||
erdp_reg &= ERST_PTR_MASK;
|
||||
erdp_reg |= ((u64) deq & (u64) ~ERST_PTR_MASK);
|
||||
}
|
||||
|
||||
/* Clear the event handler busy flag (RW1C); event ring is empty. */
|
||||
erdp_reg |= ERST_EHB;
|
||||
xhci_write_64(xhci, erdp_reg,
|
||||
&xhci->sec_ir_set[intr_num]->erst_dequeue);
|
||||
}
|
||||
|
||||
int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned int intr_num)
|
||||
{
|
||||
int size;
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
|
||||
if (intr_num >= xhci->max_interrupters) {
|
||||
xhci_err(xhci, "invalid secondary interrupter num %d\n",
|
||||
intr_num);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size =
|
||||
sizeof(struct xhci_erst_entry)*(xhci->sec_erst[intr_num].num_entries);
|
||||
if (xhci->sec_erst[intr_num].entries) {
|
||||
xhci_handle_sec_intr_events(xhci, intr_num);
|
||||
dma_free_coherent(dev, size, xhci->sec_erst[intr_num].entries,
|
||||
xhci->sec_erst[intr_num].erst_dma_addr);
|
||||
xhci->sec_erst[intr_num].entries = NULL;
|
||||
}
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed SEC ERST#%d",
|
||||
intr_num);
|
||||
if (xhci->sec_event_ring[intr_num])
|
||||
xhci_ring_free(xhci, xhci->sec_event_ring[intr_num]);
|
||||
|
||||
xhci->sec_event_ring[intr_num] = NULL;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Freed sec event ring");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xhci_event_ring_cleanup(struct xhci_hcd *xhci)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* sec event ring clean up */
|
||||
for (i = 1; i < xhci->max_interrupters; i++)
|
||||
xhci_sec_event_ring_cleanup(xhci_to_hcd(xhci), i);
|
||||
|
||||
kfree(xhci->sec_ir_set);
|
||||
xhci->sec_ir_set = NULL;
|
||||
kfree(xhci->sec_erst);
|
||||
xhci->sec_erst = NULL;
|
||||
kfree(xhci->sec_event_ring);
|
||||
xhci->sec_event_ring = NULL;
|
||||
|
||||
/* primary event ring clean up */
|
||||
xhci_free_erst(xhci, &xhci->erst);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary ERST");
|
||||
if (xhci->event_ring)
|
||||
xhci_ring_free(xhci, xhci->event_ring);
|
||||
xhci->event_ring = NULL;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed priamry event ring");
|
||||
}
|
||||
|
||||
void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
@@ -1844,12 +1975,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
||||
|
||||
cancel_delayed_work_sync(&xhci->cmd_timer);
|
||||
|
||||
xhci_free_erst(xhci, &xhci->erst);
|
||||
|
||||
if (xhci->event_ring)
|
||||
xhci_ring_free(xhci, xhci->event_ring);
|
||||
xhci->event_ring = NULL;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring");
|
||||
xhci_event_ring_cleanup(xhci);
|
||||
|
||||
if (xhci->lpm_command)
|
||||
xhci_free_command(xhci, xhci->lpm_command);
|
||||
@@ -2094,30 +2220,6 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
|
||||
{
|
||||
u64 temp;
|
||||
dma_addr_t deq;
|
||||
|
||||
deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
|
||||
xhci->event_ring->dequeue);
|
||||
if (deq == 0 && !in_interrupt())
|
||||
xhci_warn(xhci, "WARN something wrong with SW event ring "
|
||||
"dequeue ptr.\n");
|
||||
/* Update HC event ring dequeue pointer */
|
||||
temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
|
||||
temp &= ERST_PTR_MASK;
|
||||
/* Don't clear the EHB bit (which is RW1C) because
|
||||
* there might be more events to service.
|
||||
*/
|
||||
temp &= ~ERST_EHB;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Write event ring dequeue pointer, "
|
||||
"preserving EHB bit");
|
||||
xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
|
||||
&xhci->ir_set->erst_dequeue);
|
||||
}
|
||||
|
||||
static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
|
||||
__le32 __iomem *addr, int max_caps)
|
||||
{
|
||||
@@ -2381,6 +2483,154 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_event_ring_setup(struct xhci_hcd *xhci, struct xhci_ring **er,
|
||||
struct xhci_intr_reg __iomem *ir_set, struct xhci_erst *erst,
|
||||
unsigned int intr_num, gfp_t flags)
|
||||
{
|
||||
dma_addr_t deq;
|
||||
u64 val_64;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
*er = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, 0, flags);
|
||||
if (!*er)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = xhci_alloc_erst(xhci, *er, erst, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"intr# %d: num segs = %i, virt addr = %pK, dma addr = 0x%llx",
|
||||
intr_num,
|
||||
erst->num_entries,
|
||||
erst->entries,
|
||||
(unsigned long long)erst->erst_dma_addr);
|
||||
|
||||
/* set ERST count with the number of entries in the segment table */
|
||||
val = readl_relaxed(&ir_set->erst_size);
|
||||
val &= ERST_SIZE_MASK;
|
||||
val |= ERST_NUM_SEGS;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Write ERST size = %i to ir_set %d (some bits preserved)", val,
|
||||
intr_num);
|
||||
writel_relaxed(val, &ir_set->erst_size);
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"intr# %d: Set ERST entries to point to event ring.",
|
||||
intr_num);
|
||||
/* set the segment table base address */
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Set ERST base address for ir_set %d = 0x%llx",
|
||||
intr_num,
|
||||
(unsigned long long)erst->erst_dma_addr);
|
||||
val_64 = xhci_read_64(xhci, &ir_set->erst_base);
|
||||
val_64 &= ERST_PTR_MASK;
|
||||
val_64 |= (erst->erst_dma_addr & (u64) ~ERST_PTR_MASK);
|
||||
xhci_write_64(xhci, val_64, &ir_set->erst_base);
|
||||
|
||||
/* Set the event ring dequeue address */
|
||||
deq = xhci_trb_virt_to_dma((*er)->deq_seg, (*er)->dequeue);
|
||||
if (deq == 0 && !in_interrupt())
|
||||
xhci_warn(xhci,
|
||||
"intr# %d:WARN something wrong with SW event ring deq ptr.\n",
|
||||
intr_num);
|
||||
/* Update HC event ring dequeue pointer */
|
||||
val_64 = xhci_read_64(xhci, &ir_set->erst_dequeue);
|
||||
val_64 &= ERST_PTR_MASK;
|
||||
/* Don't clear the EHB bit (which is RW1C) because
|
||||
* there might be more events to service.
|
||||
*/
|
||||
val_64 &= ~ERST_EHB;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"intr# %d:Write event ring dequeue pointer, preserving EHB bit",
|
||||
intr_num);
|
||||
xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | val_64,
|
||||
&ir_set->erst_dequeue);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Wrote ERST address to ir_set %d.", intr_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_sec_event_ring_setup(struct usb_hcd *hcd, unsigned int intr_num)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
||||
if ((xhci->xhc_state & XHCI_STATE_HALTED) || !xhci->sec_ir_set
|
||||
|| !xhci->sec_event_ring || !xhci->sec_erst ||
|
||||
intr_num >= xhci->max_interrupters) {
|
||||
xhci_err(xhci,
|
||||
"%s:state %x ir_set %pK evt_ring %pK erst %pK intr# %d\n",
|
||||
__func__, xhci->xhc_state, xhci->sec_ir_set,
|
||||
xhci->sec_event_ring, xhci->sec_erst, intr_num);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (xhci->sec_event_ring && xhci->sec_event_ring[intr_num]
|
||||
&& xhci->sec_event_ring[intr_num]->first_seg)
|
||||
goto done;
|
||||
|
||||
xhci->sec_ir_set[intr_num] = &xhci->run_regs->ir_set[intr_num];
|
||||
ret = xhci_event_ring_setup(xhci,
|
||||
&xhci->sec_event_ring[intr_num],
|
||||
xhci->sec_ir_set[intr_num],
|
||||
&xhci->sec_erst[intr_num],
|
||||
intr_num, GFP_KERNEL);
|
||||
if (ret) {
|
||||
xhci_err(xhci, "sec event ring setup failed inter#%d\n",
|
||||
intr_num);
|
||||
return ret;
|
||||
}
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_event_ring_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* primary + secondary */
|
||||
xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1);
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Allocating primary event ring");
|
||||
|
||||
/* Set ir_set to interrupt register set 0 */
|
||||
xhci->ir_set = &xhci->run_regs->ir_set[0];
|
||||
ret = xhci_event_ring_setup(xhci, &xhci->event_ring, xhci->ir_set,
|
||||
&xhci->erst, 0, flags);
|
||||
if (ret) {
|
||||
xhci_err(xhci, "failed to setup primary event ring\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Allocating sec event ring related pointers");
|
||||
|
||||
xhci->sec_ir_set = kcalloc(xhci->max_interrupters,
|
||||
sizeof(*xhci->sec_ir_set), flags);
|
||||
if (!xhci->sec_ir_set) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
xhci->sec_event_ring = kcalloc(xhci->max_interrupters,
|
||||
sizeof(*xhci->sec_event_ring), flags);
|
||||
if (!xhci->sec_event_ring) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
xhci->sec_erst = kcalloc(xhci->max_interrupters,
|
||||
sizeof(*xhci->sec_erst), flags);
|
||||
if (!xhci->sec_erst)
|
||||
ret = -ENOMEM;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
{
|
||||
dma_addr_t dma;
|
||||
@@ -2388,7 +2638,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
unsigned int val, val2;
|
||||
u64 val_64;
|
||||
u32 page_size, temp;
|
||||
int i, ret;
|
||||
int i;
|
||||
|
||||
INIT_LIST_HEAD(&xhci->cmd_list);
|
||||
|
||||
@@ -2509,50 +2759,17 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
"// Doorbell array is located at offset 0x%x"
|
||||
" from cap regs base addr", val);
|
||||
xhci->dba = (void __iomem *) xhci->cap_regs + val;
|
||||
/* Set ir_set to interrupt register set 0 */
|
||||
xhci->ir_set = &xhci->run_regs->ir_set[0];
|
||||
|
||||
/*
|
||||
* Event ring setup: Allocate a normal ring, but also setup
|
||||
* the event ring segment table (ERST). Section 4.9.3.
|
||||
*/
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring");
|
||||
xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
|
||||
0, flags);
|
||||
if (!xhci->event_ring)
|
||||
if (xhci_event_ring_init(xhci, GFP_KERNEL))
|
||||
goto fail;
|
||||
|
||||
if (xhci_check_trb_in_td_math(xhci) < 0)
|
||||
goto fail;
|
||||
|
||||
ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* set ERST count with the number of entries in the segment table */
|
||||
val = readl(&xhci->ir_set->erst_size);
|
||||
val &= ERST_SIZE_MASK;
|
||||
val |= ERST_NUM_SEGS;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Write ERST size = %i to ir_set 0 (some bits preserved)",
|
||||
val);
|
||||
writel(val, &xhci->ir_set->erst_size);
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Set ERST entries to point to event ring.");
|
||||
/* set the segment table base address */
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Set ERST base address for ir_set 0 = 0x%llx",
|
||||
(unsigned long long)xhci->erst.erst_dma_addr);
|
||||
val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base);
|
||||
val_64 &= ERST_PTR_MASK;
|
||||
val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK);
|
||||
xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base);
|
||||
|
||||
/* Set the event ring dequeue address */
|
||||
xhci_set_hc_event_deq(xhci);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Wrote ERST address to ir_set 0.");
|
||||
|
||||
/*
|
||||
* XXX: Might need to set the Interrupter Moderation Register to
|
||||
* something other than the default (~1ms minimum between interrupts).
|
||||
|
||||
@@ -5243,6 +5243,8 @@ static const struct hc_driver xhci_hc_driver = {
|
||||
.enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout,
|
||||
.disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
|
||||
.find_raw_port_number = xhci_find_raw_port_number,
|
||||
.sec_event_ring_setup = xhci_sec_event_ring_setup,
|
||||
.sec_event_ring_cleanup = xhci_sec_event_ring_cleanup,
|
||||
};
|
||||
|
||||
void xhci_init_driver(struct hc_driver *drv,
|
||||
|
||||
@@ -1741,6 +1741,8 @@ struct xhci_hcd {
|
||||
struct xhci_doorbell_array __iomem *dba;
|
||||
/* Our HCD's current interrupter register set */
|
||||
struct xhci_intr_reg __iomem *ir_set;
|
||||
/* secondary interrupter */
|
||||
struct xhci_intr_reg __iomem **sec_ir_set;
|
||||
|
||||
int core_id;
|
||||
|
||||
@@ -1786,6 +1788,11 @@ struct xhci_hcd {
|
||||
struct xhci_command *current_cmd;
|
||||
struct xhci_ring *event_ring;
|
||||
struct xhci_erst erst;
|
||||
|
||||
/* secondary event ring and erst */
|
||||
struct xhci_ring **sec_event_ring;
|
||||
struct xhci_erst *sec_erst;
|
||||
|
||||
/* Scratchpad */
|
||||
struct xhci_scratchpad *scratchpad;
|
||||
/* Store LPM test failed devices' information */
|
||||
@@ -2054,6 +2061,8 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
||||
int type, gfp_t flags);
|
||||
void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
int xhci_sec_event_ring_setup(struct usb_hcd *hcd, unsigned int intr_num);
|
||||
int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned int intr_num);
|
||||
|
||||
/* xHCI host controller glue */
|
||||
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
|
||||
|
||||
@@ -824,6 +824,10 @@ static inline bool usb_device_no_sg_constraint(struct usb_device *udev)
|
||||
|
||||
/* for drivers using iso endpoints */
|
||||
extern int usb_get_current_frame_number(struct usb_device *usb_dev);
|
||||
extern int usb_sec_event_ring_setup(struct usb_device *dev,
|
||||
unsigned int intr_num);
|
||||
extern int usb_sec_event_ring_cleanup(struct usb_device *dev,
|
||||
unsigned int intr_num);
|
||||
extern int usb_get_controller_id(struct usb_device *dev);
|
||||
|
||||
/* Sets up a group of bulk endpoints to support multiple stream IDs. */
|
||||
|
||||
@@ -406,6 +406,10 @@ struct hc_driver {
|
||||
int (*find_raw_port_number)(struct usb_hcd *, int);
|
||||
/* Call for power on/off the port if necessary */
|
||||
int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
|
||||
|
||||
int (*sec_event_ring_setup)(struct usb_hcd *hcd, unsigned int intr_num);
|
||||
int (*sec_event_ring_cleanup)(struct usb_hcd *hcd,
|
||||
unsigned int intr_num);
|
||||
int (*get_core_id)(struct usb_hcd *hcd);
|
||||
|
||||
};
|
||||
@@ -446,6 +450,10 @@ extern int usb_hcd_alloc_bandwidth(struct usb_device *udev,
|
||||
struct usb_host_interface *old_alt,
|
||||
struct usb_host_interface *new_alt);
|
||||
extern int usb_hcd_get_frame_number(struct usb_device *udev);
|
||||
extern int usb_hcd_sec_event_ring_setup(struct usb_device *udev,
|
||||
unsigned int intr_num);
|
||||
extern int usb_hcd_sec_event_ring_cleanup(struct usb_device *udev,
|
||||
unsigned int intr_num);
|
||||
extern int usb_hcd_get_controller_id(struct usb_device *udev);
|
||||
|
||||
struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
|
||||
|
||||
Reference in New Issue
Block a user