icE1usb fw: E1 core config update depending on state

Store Rx/Tx config separately so we can change some of the config
bit depending on current state while still keeping what the user
asked for.

Not really used ATM since state is never IDLE, but for future use.

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Change-Id: I0b4cbf88abc4af801054ba5d6779dede5649852a
diff --git a/firmware/ice40-riscv/icE1usb/e1.c b/firmware/ice40-riscv/icE1usb/e1.c
index 09bc6d2..964b0bc 100644
--- a/firmware/ice40-riscv/icE1usb/e1.c
+++ b/firmware/ice40-riscv/icE1usb/e1.c
@@ -234,14 +234,20 @@
 
 struct e1_state {
 	struct {
-		uint32_t cr;
+		struct {
+			uint32_t cfg;
+			uint32_t val;
+		} cr;
 		struct e1_fifo fifo;
 		int in_flight;
 		enum e1_pipe_state state;
 	} rx;
 
 	struct {
-		uint32_t cr;
+		struct {
+			uint32_t cfg;
+			uint32_t val;
+		} cr;
 		struct e1_fifo fifo;
 		int in_flight;
 		enum e1_pipe_state state;
@@ -280,6 +286,30 @@
 		E1_TX_CR_LOOPBACK |		\
 		E1_TX_CR_LOOPBACK_CROSS )
 
+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;
+	}
+}
+
 void
 e1_init(int port, uint16_t rx_cr, uint16_t tx_cr)
 {
@@ -293,17 +323,18 @@
 	e1f_init(&e1->rx.fifo, (512 * port) +   0, 256);
 	e1f_init(&e1->tx.fifo, (512 * port) + 256, 256);
 
-	/* Enable Rx */
-	e1->rx.cr = E1_RX_CR_ENABLE | (rx_cr & RXCR_PERMITTED);
-	e1_regs->rx.csr = E1_RX_CR_OVFL_CLR | e1->rx.cr;
-
-	/* Enable Tx */
-	e1->tx.cr = E1_TX_CR_ENABLE | (tx_cr & TXCR_PERMITTED);
-	e1_regs->tx.csr = E1_TX_CR_UNFL_CLR | e1->tx.cr;
-
-	/* State */
+	/* Flow state */
 	e1->rx.state = BOOT;
 	e1->tx.state = BOOT;
+
+	/* 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;
 }
 
 void
@@ -311,8 +342,9 @@
 {
 	volatile struct e1_core *e1_regs = _get_regs(port);
 	struct e1_state *e1 = _get_state(port);
-	e1->rx.cr = (e1->rx.cr & ~RXCR_PERMITTED) | (cr & RXCR_PERMITTED);
-	e1_regs->rx.csr = e1->rx.cr;
+	e1->rx.cr.cfg = cr & RXCR_PERMITTED;
+	_e1_update_cr_val(port);
+	e1_regs->rx.csr = e1->rx.cr.val;
 }
 
 void
@@ -320,8 +352,9 @@
 {
 	volatile struct e1_core *e1_regs = _get_regs(port);
 	struct e1_state *e1 = _get_state(port);
-	e1->tx.cr = (e1->tx.cr & ~TXCR_PERMITTED) | (cr & TXCR_PERMITTED);
-	e1_regs->tx.csr = e1->tx.cr;
+	e1->tx.cr.cfg = cr & TXCR_PERMITTED;
+	_e1_update_cr_val(port);
+	e1_regs->tx.csr = e1->tx.cr.val;
 }
 
 unsigned int
@@ -523,7 +556,7 @@
 
 		/* Clear overflow if needed */
 	if (e1->rx.state != RUN) {
-		e1_regs->rx.csr = e1->rx.cr | E1_RX_CR_OVFL_CLR;
+		e1_regs->rx.csr = e1->rx.cr.val | E1_RX_CR_OVFL_CLR;
 		e1->rx.state = RUN;
 	}
 done_rx:
@@ -554,7 +587,7 @@
 
 		/* Clear underflow if needed */
 	if (e1->tx.state != RUN) {
-		e1_regs->tx.csr = e1->tx.cr | E1_TX_CR_UNFL_CLR;
+		e1_regs->tx.csr = e1->tx.cr.val | E1_TX_CR_UNFL_CLR;
 		e1->tx.state = RUN;
 	}
 }