2020-09-14 08:22:29 +00:00
|
|
|
/*
|
|
|
|
* e1.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
|
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "console.h"
|
|
|
|
#include "e1.h"
|
2020-12-15 22:32:53 +00:00
|
|
|
#include "e1_hw.h"
|
2020-09-14 08:22:29 +00:00
|
|
|
|
|
|
|
#include "dma.h"
|
|
|
|
#include "led.h" // FIXME
|
2022-01-03 21:12:59 +00:00
|
|
|
#include "utils.h"
|
2020-09-14 08:22:29 +00:00
|
|
|
|
|
|
|
|
2022-01-03 17:48:08 +00:00
|
|
|
// HW access
|
|
|
|
// ---------
|
|
|
|
|
2022-01-03 21:12:59 +00:00
|
|
|
static volatile struct e1_core * const e1_regs_base = (void *)(E1_CORE_BASE);
|
2020-09-14 08:22:29 +00:00
|
|
|
static volatile uint8_t * const e1_data = (void *)(E1_DATA_BASE);
|
|
|
|
|
2022-01-03 17:48:08 +00:00
|
|
|
|
|
|
|
// Helpers
|
|
|
|
// -------
|
|
|
|
|
2022-01-03 17:45:53 +00:00
|
|
|
static unsigned int
|
2020-09-14 08:22:29 +00:00
|
|
|
e1_data_ofs(int mf, int frame, int ts)
|
|
|
|
{
|
|
|
|
return (mf << 9) | (frame << 5) | ts;
|
|
|
|
}
|
|
|
|
|
2022-01-03 17:45:53 +00:00
|
|
|
static volatile uint8_t *
|
2020-12-14 16:02:13 +00:00
|
|
|
e1_data_ptr(int mf, int frame, int ts)
|
|
|
|
{
|
|
|
|
return &e1_data[e1_data_ofs(mf, frame, ts)];
|
|
|
|
}
|
2020-09-14 08:22:29 +00:00
|
|
|
|
2022-01-03 17:48:08 +00:00
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
// FIFOs
|
|
|
|
// -----
|
|
|
|
/* Note: FIFO works at 'frame' level (i.e. 32 bytes) */
|
|
|
|
|
|
|
|
struct e1_fifo {
|
|
|
|
/* Buffer zone associated with the FIFO */
|
|
|
|
unsigned int base;
|
|
|
|
unsigned int mask;
|
|
|
|
|
|
|
|
/* Pointers / Levels */
|
|
|
|
unsigned int wptr[2]; /* 0=committed 1=allocated */
|
|
|
|
unsigned int rptr[2]; /* 0=discared 1=peeked */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Utils */
|
|
|
|
static void
|
2022-01-10 11:44:53 +00:00
|
|
|
e1f_init(struct e1_fifo *fifo, unsigned int base, unsigned int len)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
|
|
|
memset(fifo, 0x00, sizeof(struct e1_fifo));
|
|
|
|
fifo->base = base;
|
|
|
|
fifo->mask = len - 1;
|
|
|
|
}
|
|
|
|
|
2022-01-10 11:44:53 +00:00
|
|
|
static void
|
|
|
|
e1f_reset(struct e1_fifo *fifo)
|
|
|
|
{
|
|
|
|
fifo->wptr[0] = fifo->wptr[1] = 0;
|
|
|
|
fifo->rptr[0] = fifo->rptr[1] = 0;
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
static unsigned int
|
|
|
|
e1f_allocd_frames(struct e1_fifo *fifo)
|
|
|
|
{
|
|
|
|
/* Number of frames that are allocated (i.e. where we can't write to) */
|
|
|
|
return (fifo->wptr[1] - fifo->rptr[0]) & fifo->mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
e1f_valid_frames(struct e1_fifo *fifo)
|
|
|
|
{
|
|
|
|
/* Number of valid frames */
|
|
|
|
return (fifo->wptr[0] - fifo->rptr[0]) & fifo->mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
e1f_unseen_frames(struct e1_fifo *fifo)
|
|
|
|
{
|
|
|
|
/* Number of valid frames that haven't been peeked yet */
|
|
|
|
return (fifo->wptr[0] - fifo->rptr[1]) & fifo->mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
e1f_free_frames(struct e1_fifo *fifo)
|
|
|
|
{
|
|
|
|
/* Number of frames that aren't allocated */
|
|
|
|
return (fifo->rptr[0] - fifo->wptr[1] - 1) & fifo->mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
e1f_ofs_to_dma(unsigned int ofs)
|
|
|
|
{
|
|
|
|
/* DMA address are 32-bits word address. Offsets are 32 byte address */
|
|
|
|
return (ofs << 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
e1f_ofs_to_mf(unsigned int ofs)
|
|
|
|
{
|
|
|
|
/* E1 Buffer Descriptors are always multiframe aligned */
|
|
|
|
return (ofs >> 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Debug */
|
|
|
|
static void
|
|
|
|
e1f_debug(struct e1_fifo *fifo, const char *name)
|
|
|
|
{
|
|
|
|
unsigned int la, lv, lu, lf;
|
|
|
|
|
|
|
|
la = e1f_allocd_frames(fifo);
|
|
|
|
lv = e1f_valid_frames(fifo);
|
|
|
|
lu = e1f_unseen_frames(fifo);
|
|
|
|
lf = e1f_free_frames(fifo);
|
|
|
|
|
|
|
|
printf("%s: R: %u / %u | W: %u / %u | A:%u V:%u U:%u F:%u\n",
|
|
|
|
name,
|
|
|
|
fifo->rptr[0], fifo->rptr[1], fifo->wptr[0], fifo->wptr[1],
|
|
|
|
la, lv, lu, lf
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Frame level read/write */
|
|
|
|
static unsigned int
|
|
|
|
e1f_frame_write(struct e1_fifo *fifo, unsigned int *ofs, unsigned int max_frames)
|
|
|
|
{
|
|
|
|
unsigned int lf, le;
|
|
|
|
|
|
|
|
lf = e1f_free_frames(fifo);
|
|
|
|
le = fifo->mask - fifo->wptr[0] + 1;
|
|
|
|
|
|
|
|
if (max_frames > le)
|
|
|
|
max_frames = le;
|
|
|
|
if (max_frames > lf)
|
|
|
|
max_frames = lf;
|
|
|
|
|
|
|
|
*ofs = fifo->base + fifo->wptr[0];
|
|
|
|
fifo->wptr[1] = fifo->wptr[0] = (fifo->wptr[0] + max_frames) & fifo->mask;
|
|
|
|
|
|
|
|
return max_frames;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
2020-10-29 12:24:50 +00:00
|
|
|
e1f_frame_read(struct e1_fifo *fifo, unsigned int *ofs, unsigned int max_frames)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
|
|
|
unsigned int lu, le;
|
|
|
|
|
|
|
|
lu = e1f_unseen_frames(fifo);
|
|
|
|
le = fifo->mask - fifo->rptr[1] + 1;
|
|
|
|
|
|
|
|
if (max_frames > le)
|
|
|
|
max_frames = le;
|
|
|
|
if (max_frames > lu)
|
|
|
|
max_frames = lu;
|
|
|
|
|
|
|
|
*ofs = fifo->base + fifo->rptr[1];
|
|
|
|
fifo->rptr[0] = fifo->rptr[1] = (fifo->rptr[1] + max_frames) & fifo->mask;
|
|
|
|
|
|
|
|
return max_frames;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* MultiFrame level split read/write */
|
|
|
|
static bool
|
|
|
|
e1f_multiframe_write_prepare(struct e1_fifo *fifo, unsigned int *ofs)
|
|
|
|
{
|
|
|
|
unsigned int lf;
|
|
|
|
|
|
|
|
lf = e1f_free_frames(fifo);
|
|
|
|
if (lf < 16)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
*ofs = fifo->base + fifo->wptr[1];
|
|
|
|
fifo->wptr[1] = (fifo->wptr[1] + 16) & fifo->mask;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
e1f_multiframe_write_commit(struct e1_fifo *fifo)
|
|
|
|
{
|
|
|
|
fifo->wptr[0] = (fifo->wptr[0] + 16) & fifo->mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
e1f_multiframe_read_peek(struct e1_fifo *fifo, unsigned int *ofs)
|
|
|
|
{
|
|
|
|
unsigned int lu;
|
|
|
|
|
|
|
|
lu = e1f_unseen_frames(fifo);
|
|
|
|
if (lu < 16)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
*ofs = fifo->base + fifo->rptr[1];
|
|
|
|
fifo->rptr[1] = (fifo->rptr[1] + 16) & fifo->mask;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
e1f_multiframe_read_discard(struct e1_fifo *fifo)
|
|
|
|
{
|
|
|
|
fifo->rptr[0] = (fifo->rptr[0] + 16) & fifo->mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
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
2022-01-13 12:17:54 +00:00
|
|
|
e1f_multiframe_empty_tail(struct e1_fifo *fifo)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
|
|
|
fifo->rptr[0] = fifo->rptr[1] = (fifo->wptr[0] & ~15);
|
|
|
|
}
|
|
|
|
|
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
2022-01-13 12:17:54 +00:00
|
|
|
static void
|
|
|
|
e1f_multiframe_empty_head(struct e1_fifo *fifo)
|
|
|
|
{
|
|
|
|
fifo->wptr[0] = fifo->wptr[1] = ((fifo->rptr[1] + 15) & ~15);
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
|
|
|
|
// Main logic
|
|
|
|
// ----------
|
|
|
|
|
|
|
|
enum e1_pipe_state {
|
2022-01-10 12:26:15 +00:00
|
|
|
IDLE = 0, /* not running */
|
|
|
|
STARTING = 1, /* after e1_start(), waiting for priming */
|
|
|
|
RUN = 2, /* normal operation */
|
|
|
|
RECOVER = 3, /* after underflow, overflow or alignment error */
|
|
|
|
SHUTDOWN = 4, /* after e1_stop(), waiting for shutdown */
|
2020-09-14 08:22:29 +00:00
|
|
|
};
|
|
|
|
|
2022-01-03 21:12:59 +00:00
|
|
|
struct e1_state {
|
2020-09-14 08:22:29 +00:00
|
|
|
struct {
|
2022-01-10 12:18:19 +00:00
|
|
|
struct {
|
|
|
|
uint32_t cfg;
|
|
|
|
uint32_t val;
|
|
|
|
} cr;
|
2020-09-14 08:22:29 +00:00
|
|
|
struct e1_fifo fifo;
|
|
|
|
int in_flight;
|
|
|
|
enum e1_pipe_state state;
|
|
|
|
} rx;
|
|
|
|
|
|
|
|
struct {
|
2022-01-10 12:18:19 +00:00
|
|
|
struct {
|
|
|
|
uint32_t cfg;
|
|
|
|
uint32_t val;
|
|
|
|
} cr;
|
2020-09-14 08:22:29 +00:00
|
|
|
struct e1_fifo fifo;
|
|
|
|
int in_flight;
|
|
|
|
enum e1_pipe_state state;
|
|
|
|
} tx;
|
2022-01-03 17:48:08 +00:00
|
|
|
|
2020-12-14 16:31:03 +00:00
|
|
|
struct e1_error_count errors;
|
2022-01-03 21:12:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct e1_state g_e1[2];
|
|
|
|
|
|
|
|
|
|
|
|
static volatile struct e1_core *
|
|
|
|
_get_regs(int port)
|
|
|
|
{
|
|
|
|
if ((port < 0) || (port > 1))
|
|
|
|
panic("_get_regs invalid port %d", port);
|
|
|
|
return &e1_regs_base[port];
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct e1_state *
|
|
|
|
_get_state(int port)
|
|
|
|
{
|
|
|
|
if ((port < 0) || (port > 1))
|
|
|
|
panic("_get_state invalid port %d", port);
|
|
|
|
return &g_e1[port];
|
|
|
|
}
|
2020-09-14 08:22:29 +00:00
|
|
|
|
|
|
|
|
2022-01-10 11:26:20 +00:00
|
|
|
#define RXCR_PERMITTED ( \
|
|
|
|
E1_RX_CR_MODE_MASK )
|
|
|
|
|
|
|
|
#define TXCR_PERMITTED ( \
|
|
|
|
E1_TX_CR_MODE_MASK | \
|
|
|
|
E1_TX_CR_TICK_MASK | \
|
|
|
|
E1_TX_CR_ALARM | \
|
|
|
|
E1_TX_CR_LOOPBACK | \
|
|
|
|
E1_TX_CR_LOOPBACK_CROSS )
|
|
|
|
|
2022-01-10 12:18:19 +00:00
|
|
|
static void
|
|
|
|
_e1_update_cr_val(int port)
|
|
|
|
{
|
|
|
|
struct e1_state *e1 = _get_state(port);
|
|
|
|
|
|
|
|
/* RX */
|
|
|
|
if (e1->rx.state == IDLE) {
|
|
|
|
/* "Off" state: Force MFA mode to detect remote side */
|
|
|
|
e1->rx.cr.val = (e1->rx.cr.cfg & ~E1_RX_CR_MODE_MASK) | E1_RX_CR_ENABLE | E1_RX_CR_MODE_MFA;
|
|
|
|
} else {
|
|
|
|
/* "On state: Enabled + User config */
|
|
|
|
e1->rx.cr.val = e1->rx.cr.cfg | E1_RX_CR_ENABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TX */
|
|
|
|
if (e1->tx.state == IDLE) {
|
|
|
|
/* "Off" state: We TX only OIS */
|
|
|
|
e1->tx.cr.val = (e1->tx.cr.cfg & ~(E1_TX_CR_MODE_MASK | E1_TX_CR_ALARM)) | E1_TX_CR_ENABLE | E1_TX_CR_MODE_TRSP;
|
|
|
|
} else {
|
|
|
|
/* "On state: Enabled + User config */
|
|
|
|
e1->tx.cr.val = e1->tx.cr.cfg | E1_TX_CR_ENABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
void
|
2022-01-03 21:12:59 +00:00
|
|
|
e1_init(int port, uint16_t rx_cr, uint16_t tx_cr)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
volatile struct e1_core *e1_regs = _get_regs(port);
|
|
|
|
struct e1_state *e1 = _get_state(port);
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
/* Global state init */
|
2022-01-03 21:12:59 +00:00
|
|
|
memset(e1, 0x00, sizeof(struct e1_state));
|
2020-09-14 08:22:29 +00:00
|
|
|
|
2022-01-10 11:44:53 +00:00
|
|
|
/* Initialize FIFOs */
|
|
|
|
e1f_init(&e1->rx.fifo, (512 * port) + 0, 256);
|
|
|
|
e1f_init(&e1->tx.fifo, (512 * port) + 256, 256);
|
2020-09-14 08:22:29 +00:00
|
|
|
|
2022-01-10 12:18:19 +00:00
|
|
|
/* Flow state */
|
2022-01-10 12:26:15 +00:00
|
|
|
e1->rx.state = IDLE;
|
|
|
|
e1->tx.state = IDLE;
|
2022-01-10 12:18:19 +00:00
|
|
|
|
|
|
|
/* Set config registers */
|
|
|
|
e1->rx.cr.cfg = rx_cr & RXCR_PERMITTED;
|
|
|
|
e1->tx.cr.cfg = tx_cr & TXCR_PERMITTED;
|
|
|
|
|
|
|
|
_e1_update_cr_val(port);
|
|
|
|
|
|
|
|
e1_regs->rx.csr = e1->rx.cr.val;
|
|
|
|
e1_regs->tx.csr = e1->tx.cr.val;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
|
2020-12-15 23:02:11 +00:00
|
|
|
void
|
2022-01-10 11:28:28 +00:00
|
|
|
e1_rx_config(int port, uint16_t cr)
|
2020-12-15 23:02:11 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
volatile struct e1_core *e1_regs = _get_regs(port);
|
|
|
|
struct e1_state *e1 = _get_state(port);
|
2022-01-10 12:18:19 +00:00
|
|
|
e1->rx.cr.cfg = cr & RXCR_PERMITTED;
|
|
|
|
_e1_update_cr_val(port);
|
|
|
|
e1_regs->rx.csr = e1->rx.cr.val;
|
2020-12-15 23:02:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2022-01-10 11:28:28 +00:00
|
|
|
e1_tx_config(int port, uint16_t cr)
|
2020-12-15 23:02:11 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
volatile struct e1_core *e1_regs = _get_regs(port);
|
|
|
|
struct e1_state *e1 = _get_state(port);
|
2022-01-10 12:18:19 +00:00
|
|
|
e1->tx.cr.cfg = cr & TXCR_PERMITTED;
|
|
|
|
_e1_update_cr_val(port);
|
|
|
|
e1_regs->tx.csr = e1->tx.cr.val;
|
2020-12-15 23:02:11 +00:00
|
|
|
}
|
2020-09-14 08:22:29 +00:00
|
|
|
|
2022-01-10 12:26:15 +00:00
|
|
|
void
|
|
|
|
e1_start(int port)
|
|
|
|
{
|
|
|
|
volatile struct e1_core *e1_regs = _get_regs(port);
|
|
|
|
struct e1_state *e1 = _get_state(port);
|
|
|
|
|
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
2022-01-13 12:17:54 +00:00
|
|
|
/* 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;
|
|
|
|
|
|
|
|
case SHUTDOWN:
|
|
|
|
/* Shutdown is pending, go to recover which is basically
|
|
|
|
* a shutdown with auto-restart */
|
|
|
|
e1->rx.state = RECOVER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Huh ... hope for the best */
|
|
|
|
printf("[!] E1 RX start while not stopped ...\n");
|
|
|
|
}
|
2022-01-10 12:26:15 +00:00
|
|
|
|
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
2022-01-13 12:17:54 +00:00
|
|
|
/* 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");
|
|
|
|
}
|
2022-01-10 12:26:15 +00:00
|
|
|
|
|
|
|
/* Update CRs */
|
|
|
|
_e1_update_cr_val(port);
|
|
|
|
|
|
|
|
e1_regs->rx.csr = e1->rx.cr.val | E1_RX_CR_OVFL_CLR;
|
|
|
|
e1_regs->tx.csr = e1->tx.cr.val | E1_TX_CR_UNFL_CLR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
e1_stop(int port)
|
|
|
|
{
|
|
|
|
struct e1_state *e1 = _get_state(port);
|
|
|
|
|
|
|
|
/* Flow state */
|
|
|
|
e1->rx.state = SHUTDOWN;
|
|
|
|
e1->tx.state = SHUTDOWN;
|
|
|
|
|
|
|
|
/* Nothing else to do, e1_poll will stop submitting data and
|
|
|
|
* transition to IDLE when everything in-flight is done */
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
unsigned int
|
2022-01-03 21:12:59 +00:00
|
|
|
e1_rx_need_data(int port, unsigned int usb_addr, unsigned int max_frames, unsigned int *pos)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
struct e1_state *e1 = _get_state(port);
|
2022-01-01 14:22:25 +00:00
|
|
|
bool rai_received = false;
|
|
|
|
bool rai_possible = false;
|
2020-09-14 08:22:29 +00:00
|
|
|
unsigned int ofs;
|
|
|
|
int tot_frames = 0;
|
2022-01-01 14:22:25 +00:00
|
|
|
int n_frames, i;
|
2020-09-14 08:22:29 +00:00
|
|
|
|
|
|
|
while (max_frames) {
|
|
|
|
/* Get some data from the FIFO */
|
2022-01-03 21:12:59 +00:00
|
|
|
n_frames = e1f_frame_read(&e1->rx.fifo, &ofs, max_frames);
|
2020-09-14 08:22:29 +00:00
|
|
|
if (!n_frames)
|
|
|
|
break;
|
|
|
|
|
2020-12-14 16:39:23 +00:00
|
|
|
/* Give pos */
|
|
|
|
if (pos) {
|
2022-01-03 21:12:59 +00:00
|
|
|
*pos = ofs & e1->rx.fifo.mask;
|
2020-12-14 16:39:23 +00:00
|
|
|
pos = NULL;
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
/* Copy from FIFO to USB */
|
|
|
|
dma_exec(e1f_ofs_to_dma(ofs), usb_addr, n_frames * (32 / 4), false, NULL, NULL);
|
|
|
|
|
|
|
|
/* Prepare Next */
|
|
|
|
usb_addr += n_frames * (32 / 4);
|
|
|
|
max_frames -= n_frames;
|
|
|
|
tot_frames += n_frames;
|
|
|
|
|
2022-01-01 14:22:25 +00:00
|
|
|
/* While DMA is running: Determine if remote end indicates any alarms */
|
|
|
|
for (i = 0; i < n_frames; i++) {
|
|
|
|
unsigned int frame_nr = ofs + i;
|
|
|
|
/* A bit is present in every odd frame TS0 */
|
|
|
|
if (frame_nr & 1) {
|
|
|
|
uint8_t ts0 = *e1_data_ptr(0, ofs + i, 0);
|
|
|
|
rai_possible = true;
|
|
|
|
if (ts0 & 0x20) {
|
|
|
|
rai_received = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
/* Wait for DMA completion */
|
|
|
|
while (dma_poll());
|
|
|
|
}
|
|
|
|
|
2022-01-01 14:22:25 +00:00
|
|
|
if (rai_possible) {
|
|
|
|
if (rai_received) {
|
2022-01-03 21:12:59 +00:00
|
|
|
e1->errors.flags |= E1_ERR_F_RAI;
|
|
|
|
e1_platform_led_set(port, E1P_LED_YELLOW, E1P_LED_ST_ON);
|
2022-01-01 14:22:25 +00:00
|
|
|
} else {
|
2022-01-03 21:12:59 +00:00
|
|
|
e1->errors.flags &= ~E1_ERR_F_RAI;
|
|
|
|
e1_platform_led_set(port, E1P_LED_YELLOW, E1P_LED_ST_OFF);
|
2022-01-01 14:22:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
return tot_frames;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int
|
2022-01-03 21:12:59 +00:00
|
|
|
e1_tx_feed_data(int port, unsigned int usb_addr, unsigned int frames)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
struct e1_state *e1 = _get_state(port);
|
2020-09-14 08:22:29 +00:00
|
|
|
unsigned int ofs;
|
|
|
|
int n_frames;
|
|
|
|
|
|
|
|
while (frames) {
|
|
|
|
/* Get some space in FIFO */
|
2022-01-03 21:12:59 +00:00
|
|
|
n_frames = e1f_frame_write(&e1->tx.fifo, &ofs, frames);
|
2020-09-14 08:22:29 +00:00
|
|
|
if (!n_frames) {
|
2022-01-03 21:12:59 +00:00
|
|
|
printf("[!] TX FIFO Overflow (port=%d, req=%d, done=%d)\n", port, frames, n_frames);
|
|
|
|
e1f_debug(&e1->tx.fifo, "TX");
|
2020-09-14 08:22:29 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy from USB to FIFO */
|
|
|
|
dma_exec(e1f_ofs_to_dma(ofs), usb_addr, n_frames * (32 / 4), true, NULL, NULL);
|
|
|
|
|
|
|
|
/* Prepare next */
|
|
|
|
usb_addr += n_frames * (32 / 4);
|
|
|
|
frames -= n_frames;
|
|
|
|
|
|
|
|
/* Wait for DMA completion */
|
|
|
|
while (dma_poll());
|
|
|
|
}
|
|
|
|
|
|
|
|
return frames;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int
|
2022-01-10 11:28:28 +00:00
|
|
|
e1_rx_level(int port)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
struct e1_state *e1 = _get_state(port);
|
2022-01-10 11:28:28 +00:00
|
|
|
return e1f_valid_frames(&e1->rx.fifo);
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int
|
2022-01-10 11:28:28 +00:00
|
|
|
e1_tx_level(int port)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
struct e1_state *e1 = _get_state(port);
|
2022-01-10 11:28:28 +00:00
|
|
|
return e1f_valid_frames(&e1->tx.fifo);
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
|
2020-12-14 16:31:03 +00:00
|
|
|
const struct e1_error_count *
|
2022-01-03 21:12:59 +00:00
|
|
|
e1_get_error_count(int port)
|
2020-12-14 16:31:03 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
struct e1_state *e1 = _get_state(port);
|
|
|
|
return &e1->errors;
|
2020-12-14 16:31:03 +00:00
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
void
|
2022-01-03 21:12:59 +00:00
|
|
|
e1_poll(int port)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
volatile struct e1_core *e1_regs = _get_regs(port);
|
|
|
|
struct e1_state *e1 = _get_state(port);
|
2020-09-14 08:22:29 +00:00
|
|
|
uint32_t bd;
|
|
|
|
unsigned int ofs;
|
|
|
|
|
|
|
|
/* HACK: LED link status */
|
2020-12-15 17:35:42 +00:00
|
|
|
if (e1_regs->rx.csr & E1_RX_SR_ALIGNED) {
|
2022-01-03 21:12:59 +00:00
|
|
|
e1_platform_led_set(port, E1P_LED_GREEN, E1P_LED_ST_ON);
|
2020-09-14 08:22:29 +00:00
|
|
|
led_color(0, 48, 0);
|
2022-01-03 21:12:59 +00:00
|
|
|
e1->errors.flags &= ~(E1_ERR_F_LOS|E1_ERR_F_ALIGN_ERR);
|
2020-12-15 17:35:42 +00:00
|
|
|
} else {
|
2022-01-03 21:12:59 +00:00
|
|
|
e1_platform_led_set(port, E1P_LED_GREEN, E1P_LED_ST_BLINK);
|
2022-01-06 21:00:50 +00:00
|
|
|
e1_platform_led_set(port, E1P_LED_YELLOW, E1P_LED_ST_OFF);
|
2020-09-14 08:22:29 +00:00
|
|
|
led_color(48, 0, 0);
|
2022-01-03 21:12:59 +00:00
|
|
|
e1->errors.flags |= E1_ERR_F_ALIGN_ERR;
|
2020-12-14 16:31:03 +00:00
|
|
|
/* TODO: completely off if rx tick counter not incrementing */
|
2020-12-15 17:35:42 +00:00
|
|
|
}
|
2020-09-14 08:22:29 +00:00
|
|
|
|
2022-01-10 11:46:49 +00:00
|
|
|
/* Active ? */
|
|
|
|
if ((e1->rx.state == IDLE) && (e1->tx.state == IDLE))
|
|
|
|
return;
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
/* Recover any done TX BD */
|
|
|
|
while ( (bd = e1_regs->tx.bd) & E1_BD_VALID ) {
|
2022-01-03 21:12:59 +00:00
|
|
|
e1f_multiframe_read_discard(&e1->tx.fifo);
|
|
|
|
e1->tx.in_flight--;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Recover any done RX BD */
|
|
|
|
while ( (bd = e1_regs->rx.bd) & E1_BD_VALID ) {
|
|
|
|
/* FIXME: CRC status ? */
|
2022-01-03 21:12:59 +00:00
|
|
|
e1f_multiframe_write_commit(&e1->rx.fifo);
|
2020-12-14 16:31:03 +00:00
|
|
|
if ((bd & (E1_BD_CRC0 | E1_BD_CRC1)) != (E1_BD_CRC0 | E1_BD_CRC1)) {
|
2022-01-03 21:12:59 +00:00
|
|
|
printf("[!] E1 crc err (port=%d, bd=%03x)\n", port, bd);
|
|
|
|
e1->errors.crc++;
|
2020-12-14 16:31:03 +00:00
|
|
|
}
|
2022-01-03 21:12:59 +00:00
|
|
|
e1->rx.in_flight--;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Boot procedure */
|
2022-01-10 12:26:15 +00:00
|
|
|
if (e1->tx.state == STARTING) {
|
2022-01-03 21:12:59 +00:00
|
|
|
if (e1f_unseen_frames(&e1->tx.fifo) < (16 * 5))
|
2020-09-14 08:22:29 +00:00
|
|
|
return;
|
|
|
|
/* HACK: LED flow status */
|
|
|
|
led_blink(true, 200, 1000);
|
|
|
|
led_breathe(true, 100, 200);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle RX */
|
2022-01-10 12:26:15 +00:00
|
|
|
/* Bypass if OFF */
|
|
|
|
if (e1->rx.state == IDLE)
|
|
|
|
goto done_rx;
|
|
|
|
|
|
|
|
/* Shutdown */
|
|
|
|
if (e1->rx.state == SHUTDOWN) {
|
|
|
|
if (e1->rx.in_flight == 0) {
|
|
|
|
e1->rx.state = IDLE;
|
|
|
|
_e1_update_cr_val(port);
|
|
|
|
e1_regs->rx.csr = e1->rx.cr.val;
|
|
|
|
}
|
|
|
|
goto done_rx;
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
/* Misalign ? */
|
2022-01-03 21:12:59 +00:00
|
|
|
if (e1->rx.state == RUN) {
|
2020-09-14 08:22:29 +00:00
|
|
|
if (!(e1_regs->rx.csr & E1_RX_SR_ALIGNED)) {
|
2022-01-03 21:12:59 +00:00
|
|
|
printf("[!] E1 rx misalign (port=%d)\n", port);
|
|
|
|
e1->rx.state = RECOVER;
|
|
|
|
e1->errors.align++;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Overflow ? */
|
2022-01-03 21:12:59 +00:00
|
|
|
if (e1->rx.state == RUN) {
|
2020-09-14 08:22:29 +00:00
|
|
|
if (e1_regs->rx.csr & E1_RX_SR_OVFL) {
|
2022-01-03 21:12:59 +00:00
|
|
|
printf("[!] E1 overflow (port=%d, inf=%d)\n", port, e1->rx.in_flight);
|
|
|
|
e1->rx.state = RECOVER;
|
|
|
|
e1->errors.ovfl++;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Recover ready ? */
|
2022-01-03 21:12:59 +00:00
|
|
|
if (e1->rx.state == RECOVER) {
|
|
|
|
if (e1->rx.in_flight != 0)
|
2020-09-14 08:22:29 +00:00
|
|
|
goto done_rx;
|
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
2022-01-13 12:17:54 +00:00
|
|
|
e1f_multiframe_empty_tail(&e1->rx.fifo);
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill new RX BD */
|
2022-01-03 21:12:59 +00:00
|
|
|
while (e1->rx.in_flight < 4) {
|
|
|
|
if (!e1f_multiframe_write_prepare(&e1->rx.fifo, &ofs))
|
2020-09-14 08:22:29 +00:00
|
|
|
break;
|
|
|
|
e1_regs->rx.bd = e1f_ofs_to_mf(ofs);
|
2022-01-03 21:12:59 +00:00
|
|
|
e1->rx.in_flight++;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear overflow if needed */
|
2022-01-03 21:12:59 +00:00
|
|
|
if (e1->rx.state != RUN) {
|
2022-01-10 12:18:19 +00:00
|
|
|
e1_regs->rx.csr = e1->rx.cr.val | E1_RX_CR_OVFL_CLR;
|
2022-01-03 21:12:59 +00:00
|
|
|
e1->rx.state = RUN;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
done_rx:
|
|
|
|
|
|
|
|
/* Handle TX */
|
2022-01-10 12:26:15 +00:00
|
|
|
/* Bypass if OFF */
|
|
|
|
if (e1->tx.state == IDLE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Shutdown */
|
|
|
|
if (e1->tx.state == SHUTDOWN) {
|
|
|
|
if (e1->tx.in_flight == 0) {
|
|
|
|
e1->tx.state = IDLE;
|
|
|
|
_e1_update_cr_val(port);
|
|
|
|
e1_regs->tx.csr = e1->tx.cr.val;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:22:29 +00:00
|
|
|
/* Underflow ? */
|
2022-01-03 21:12:59 +00:00
|
|
|
if (e1->tx.state == RUN) {
|
2020-09-14 08:22:29 +00:00
|
|
|
if (e1_regs->tx.csr & E1_TX_SR_UNFL) {
|
2022-01-03 21:12:59 +00:00
|
|
|
printf("[!] E1 underflow (port=%d, inf=%d)\n", port, e1->tx.in_flight);
|
|
|
|
e1->tx.state = RECOVER;
|
|
|
|
e1->errors.unfl++;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Recover ready ? */
|
2022-01-03 21:12:59 +00:00
|
|
|
if (e1->tx.state == RECOVER) {
|
|
|
|
if (e1f_unseen_frames(&e1->tx.fifo) < (16 * 5))
|
2020-09-14 08:22:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill new TX BD */
|
2022-01-03 21:12:59 +00:00
|
|
|
while (e1->tx.in_flight < 4) {
|
|
|
|
if (!e1f_multiframe_read_peek(&e1->tx.fifo, &ofs))
|
2020-09-14 08:22:29 +00:00
|
|
|
break;
|
|
|
|
e1_regs->tx.bd = e1f_ofs_to_mf(ofs);
|
2022-01-03 21:12:59 +00:00
|
|
|
e1->tx.in_flight++;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear underflow if needed */
|
2022-01-03 21:12:59 +00:00
|
|
|
if (e1->tx.state != RUN) {
|
2022-01-10 12:18:19 +00:00
|
|
|
e1_regs->tx.csr = e1->tx.cr.val | E1_TX_CR_UNFL_CLR;
|
2022-01-03 21:12:59 +00:00
|
|
|
e1->tx.state = RUN;
|
2020-09-14 08:22:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2022-01-03 21:12:59 +00:00
|
|
|
e1_debug_print(int port, bool data)
|
2020-09-14 08:22:29 +00:00
|
|
|
{
|
2022-01-03 21:12:59 +00:00
|
|
|
volatile struct e1_core *e1_regs = _get_regs(port);
|
|
|
|
struct e1_state *e1 = _get_state(port);
|
2020-09-14 08:22:29 +00:00
|
|
|
volatile uint8_t *p;
|
|
|
|
|
2022-01-03 21:12:59 +00:00
|
|
|
printf("E1 port %d\n", port);
|
2020-09-14 08:22:29 +00:00
|
|
|
printf("CSR: Rx %04x / Tx %04x\n", e1_regs->rx.csr, e1_regs->tx.csr);
|
2022-01-03 21:12:59 +00:00
|
|
|
printf("InF: Rx %d / Tx %d\n", e1->rx.in_flight, e1->tx.in_flight);
|
|
|
|
printf("Sta: Rx %d / Tx %d\n", e1->rx.state, e1->tx.state);
|
2020-09-14 08:22:29 +00:00
|
|
|
|
2022-01-03 21:12:59 +00:00
|
|
|
e1f_debug(&e1->rx.fifo, "Rx FIFO");
|
|
|
|
e1f_debug(&e1->tx.fifo, "Tx FIFO");
|
2020-09-14 08:22:29 +00:00
|
|
|
|
|
|
|
if (data) {
|
|
|
|
puts("\nE1 Data\n");
|
|
|
|
for (int f=0; f<16; f++) {
|
|
|
|
p = e1_data_ptr(0, f, 0);
|
|
|
|
for (int ts=0; ts<32; ts++)
|
|
|
|
printf(" %02x", p[ts]);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|