2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

usb: xhci: remove '0' write to write-1-to-clear register

xHCI specification 1.2, section 5.5.2.1.
Interrupt Pending bit is RW1C (Write-1-to-clear), which means that
writing '0' to is has no effect and is removed.

The Interrupt Pending (IP) bit is cleared at the start of interrupt
handling; xhci_clear_interrupt_pending(). This could theoretically
cause a new interrupt to be issued before the xhci driver reaches
the interrupter disable functions.
To address this, the IP bit is read after Interrupt Enable is
disabled, and a debug message is issued if the IP bit is still set.

Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20250515135621.335595-18-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Niklas Neronin 2025-05-15 16:56:14 +03:00 committed by Greg Kroah-Hartman
parent f5bce30ad2
commit e1db856bd2
4 changed files with 10 additions and 9 deletions

View File

@ -1907,7 +1907,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
* prevent port event interrupts from interfering
* with usb2 port resume process
*/
xhci_disable_interrupter(xhci->interrupters[0]);
xhci_disable_interrupter(xhci, xhci->interrupters[0]);
disabled_irq = true;
}
}

View File

@ -3166,7 +3166,7 @@ void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
dma_addr_t deq;
/* disable irq, ack pending interrupt and ack all pending events */
xhci_disable_interrupter(ir);
xhci_disable_interrupter(xhci, ir);
/* last acked event trb is in erdp reg */
erdp_reg = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);

View File

@ -331,7 +331,6 @@ int xhci_enable_interrupter(struct xhci_interrupter *ir)
return -EINVAL;
iman = readl(&ir->ir_set->irq_pending);
iman &= ~IMAN_IP;
iman |= IMAN_IE;
writel(iman, &ir->ir_set->irq_pending);
@ -340,7 +339,7 @@ int xhci_enable_interrupter(struct xhci_interrupter *ir)
return 0;
}
int xhci_disable_interrupter(struct xhci_interrupter *ir)
int xhci_disable_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
{
u32 iman;
@ -348,11 +347,13 @@ int xhci_disable_interrupter(struct xhci_interrupter *ir)
return -EINVAL;
iman = readl(&ir->ir_set->irq_pending);
iman &= ~IMAN_IP;
iman &= ~IMAN_IE;
writel(iman, &ir->ir_set->irq_pending);
readl(&ir->ir_set->irq_pending);
iman = readl(&ir->ir_set->irq_pending);
if (iman & IMAN_IP)
xhci_dbg(xhci, "%s: Interrupt pending\n", __func__);
return 0;
}
@ -754,7 +755,7 @@ void xhci_stop(struct usb_hcd *hcd)
"// Disabling event ring interrupts");
temp = readl(&xhci->op_regs->status);
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
xhci_disable_interrupter(ir);
xhci_disable_interrupter(xhci, ir);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory");
xhci_mem_cleanup(xhci);
@ -1189,7 +1190,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
xhci_dbg(xhci, "// Disabling event ring interrupts\n");
temp = readl(&xhci->op_regs->status);
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
xhci_disable_interrupter(xhci->interrupters[0]);
xhci_disable_interrupter(xhci, xhci->interrupters[0]);
xhci_dbg(xhci, "cleaning up memory\n");
xhci_mem_cleanup(xhci);

View File

@ -1900,7 +1900,7 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
u32 imod_interval);
int xhci_enable_interrupter(struct xhci_interrupter *ir);
int xhci_disable_interrupter(struct xhci_interrupter *ir);
int xhci_disable_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir);
/* xHCI ring, segment, TRB, and TD functions */
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);