mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
drivers/char: fix handling cable re-plug in XHCI console driver
When cable is unplugged, dbc_ensure_running() correctly detects this situation (DBC_CTRL_DCR flag is clear), and prevent sending data immediately to the device. It gets only queued in work ring buffers. When cable is plugged in again, subsequent dbc_flush() will send the buffered data. But there is a corner case, where no subsequent data was buffered in the work buffer, but a TRB was still pending. Ring the doorbell to let the controller re-send them. For console output it is rare corner case (TRB is pending for a very short time), but for console input it is very normal case (there is always one pending TRB for input). Extract doorbell ringing into separate function to avoid duplication. Signed-off-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com> Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
d88e2aa4ea
commit
7aadb75e28
1 file changed
+12
-5
@@ -546,6 +546,15 @@ static unsigned int dbc_work_ring_space_to_end(const struct dbc_work_ring *ring)
|
||||
return ring->deq - ring->enq;
|
||||
}
|
||||
|
||||
static void dbc_ring_doorbell(struct dbc *dbc, int doorbell)
|
||||
{
|
||||
uint32_t __iomem *db_reg = &dbc->dbc_reg->db;
|
||||
uint32_t db = (readl(db_reg) & ~DBC_DOORBELL_TARGET_MASK) |
|
||||
(doorbell << DBC_DOORBELL_TARGET_SHIFT);
|
||||
|
||||
writel(db, db_reg);
|
||||
}
|
||||
|
||||
static void dbc_push_trb(struct dbc *dbc, struct xhci_trb_ring *ring,
|
||||
uint64_t dma, uint64_t len)
|
||||
{
|
||||
@@ -973,6 +982,8 @@ static bool dbc_ensure_running(struct dbc *dbc)
|
||||
writel(ctrl | (1U << DBC_CTRL_DRC), ®->ctrl);
|
||||
writel(readl(®->portsc) | (1U << DBC_PSC_PED), ®->portsc);
|
||||
wmb();
|
||||
dbc_ring_doorbell(dbc, dbc->dbc_iring.db);
|
||||
dbc_ring_doorbell(dbc, dbc->dbc_oring.db);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -990,10 +1001,6 @@ static bool dbc_ensure_running(struct dbc *dbc)
|
||||
static void dbc_flush(struct dbc *dbc, struct xhci_trb_ring *trb,
|
||||
struct dbc_work_ring *wrk)
|
||||
{
|
||||
struct dbc_reg *reg = dbc->dbc_reg;
|
||||
uint32_t db = (readl(®->db) & ~DBC_DOORBELL_TARGET_MASK) |
|
||||
(trb->db << DBC_DOORBELL_TARGET_SHIFT);
|
||||
|
||||
if ( xhci_trb_ring_full(trb) )
|
||||
return;
|
||||
|
||||
@@ -1017,7 +1024,7 @@ static void dbc_flush(struct dbc *dbc, struct xhci_trb_ring *trb,
|
||||
}
|
||||
|
||||
wmb();
|
||||
writel(db, ®->db);
|
||||
dbc_ring_doorbell(dbc, trb->db);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||