icE1usb fw: Fix the E1 shutdown process

So, once BD are submitted to hw, we have to wait for them to get
back, and that's what the SHUTDOWN state is for. Now, this needs
to completete before we can call e1_start() again and so in case
it wasn't done in time, we just busy waited in e1_start().

Problem is that in the absence of a valid signal, the RX side won't
process anything and so we will wait forever.

For the TX side, if we are in remote tick config and we never had
any valid signal, this could also hang.

So we change this. There is actually no need to wait for the shutdown
to complete, we can resume where we left off.

For RX: We use the 'RECOVER' which basically waits for all pending BD
to be done, empty the fifo and auto-restarts. The only "side effect" is
that we'll loose up to 4 multiframe of data at the beginning but the
RX start is async anyway, no guarantee of where we pick up.

For TX: First we empty the FIFO from any data that wasn't already
submitted to the hardware (and is now stale) and we also use the
'RECOVER' state which will wait until the FIFO reaches nominal level
before starting feeding new data. The "side effect" here is we might
TX up to 4 multiframe of old data. Although this is rather unlikely
because the conditions for TX not to have transmitted those before
are unlikely (and hopefully go away completely with OS#5402)

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Change-Id: I3f3de5491e0999f89a63f507fafdc4af8c22c905
This commit is contained in:
Sylvain Munaut 2022-01-13 13:17:54 +01:00
parent 0cc1613f39
commit d8f33aa792
1 changed files with 46 additions and 14 deletions

View File

@ -216,11 +216,17 @@ e1f_multiframe_read_discard(struct e1_fifo *fifo)
}
static void
e1f_multiframe_empty(struct e1_fifo *fifo)
e1f_multiframe_empty_tail(struct e1_fifo *fifo)
{
fifo->rptr[0] = fifo->rptr[1] = (fifo->wptr[0] & ~15);
}
static void
e1f_multiframe_empty_head(struct e1_fifo *fifo)
{
fifo->wptr[0] = fifo->wptr[1] = ((fifo->rptr[1] + 15) & ~15);
}
// Main logic
// ----------
@ -364,21 +370,47 @@ e1_start(int port)
volatile struct e1_core *e1_regs = _get_regs(port);
struct e1_state *e1 = _get_state(port);
/* Checks */
while ((e1->rx.state == SHUTDOWN) || (e1->tx.state == SHUTDOWN))
e1_poll(port);
/* RX */
switch (e1->rx.state) {
case IDLE:
/* We're idle, clear fifo and normal start */
e1f_reset(&e1->rx.fifo);
e1->rx.state = STARTING;
break;
if ((e1->rx.state != IDLE) || (e1->tx.state != IDLE))
panic("Invalid E1 hardware state (port=%d, rxs=%d, txs=%d)",
port, e1->rx.state, e1->tx.state);
case SHUTDOWN:
/* Shutdown is pending, go to recover which is basically
* a shutdown with auto-restart */
e1->rx.state = RECOVER;
break;
/* Clear FIFOs */
e1f_reset(&e1->rx.fifo);
e1f_reset(&e1->tx.fifo);
default:
/* Huh ... hope for the best */
printf("[!] E1 RX start while not stopped ...\n");
}
/* Flow state */
e1->rx.state = STARTING;
e1->tx.state = STARTING;
/* TX */
switch (e1->tx.state) {
case IDLE:
/* We're idle, clear fifo and normal start */
e1f_reset(&e1->tx.fifo);
e1->tx.state = STARTING;
break;
case SHUTDOWN:
/* Shutdown is pending, go to recover which is basically
* a shutdown with auto-restart */
e1->tx.state = RECOVER;
/* We also prune any pending data in FIFO that's not
* already queued to hw */
e1f_multiframe_empty_head(&e1->rx.fifo);
break;
default:
/* Huh ... hope for the best */
printf("[!] E1 TX start while not stopped ...\n");
}
/* Update CRs */
_e1_update_cr_val(port);
@ -600,7 +632,7 @@ e1_poll(int port)
if (e1->rx.state == RECOVER) {
if (e1->rx.in_flight != 0)
goto done_rx;
e1f_multiframe_empty(&e1->rx.fifo);
e1f_multiframe_empty_tail(&e1->rx.fifo);
}
/* Fill new RX BD */