summaryrefslogtreecommitdiff
path: root/code/fe310/eos/net.c
diff options
context:
space:
mode:
Diffstat (limited to 'code/fe310/eos/net.c')
-rw-r--r--code/fe310/eos/net.c365
1 files changed, 365 insertions, 0 deletions
diff --git a/code/fe310/eos/net.c b/code/fe310/eos/net.c
new file mode 100644
index 0000000..080da72
--- /dev/null
+++ b/code/fe310/eos/net.c
@@ -0,0 +1,365 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "encoding.h"
+#include "platform.h"
+#include "plic/plic_driver.h"
+
+#include "eos.h"
+#include "msgq.h"
+#include "event.h"
+#include "interrupt.h"
+#include "net.h"
+#include "spi.h"
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+#define SPI_BUFQ_IDX_MASK(IDX) ((IDX) & (SPI_SIZE_BUFQ - 1))
+
+static volatile uint8_t spi_res = 0;
+static volatile uint8_t spi_res_next = 0;
+static volatile unsigned char *spi_res_buf = NULL;
+
+static EOSMsgQ spi_sndq;
+static EOSMsgItem spi_sndq_array[SPI_SIZE_BUFQ];
+
+static SPIBufQ spi_bufq;
+static unsigned char spi_bufq_array[SPI_SIZE_BUFQ][SPI_SIZE_BUF];
+
+static int spi_bufq_push(unsigned char *buffer) {
+ spi_bufq.array[SPI_BUFQ_IDX_MASK(spi_bufq.idx_w++)] = buffer;
+}
+
+static unsigned char *spi_bufq_pop(void) {
+ if (spi_bufq.idx_r == spi_bufq.idx_w) return NULL;
+ return spi_bufq.array[SPI_BUFQ_IDX_MASK(spi_bufq.idx_r++)];
+}
+
+static volatile uint8_t spi_rdy = 0;
+static volatile uint8_t spi_cts = 0;
+static volatile uint8_t spi_rts = 0;
+static SPIBuffer spi_buffer;
+
+static void spi_reset(void) {
+ int i;
+ volatile uint32_t r;
+
+ spi_cts = 0;
+ // before starting a transaction, set SPI peripheral to desired mode
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = 0;
+ while ((r = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+}
+
+static void spi_xchg(unsigned char cmd, unsigned char *buffer, uint16_t len) {
+ volatile uint32_t r1, r2;
+
+ spi_cts = 0;
+ spi_buffer.buffer = buffer;
+ spi_buffer.len_rx = 0;
+ spi_buffer.idx_tx = 0;
+ spi_buffer.idx_rx = 0;
+
+ if (spi_res_buf == NULL) {
+ if (cmd & EOS_NET_CMD_FLAG_ONEW) {
+ spi_res_next = 1;
+ } else if (spi_res) {
+ cmd |= EOS_NET_CMD_FLAG_ONEW;
+ }
+ } else if (cmd & EOS_NET_CMD_FLAG_ONEW) {
+ cmd &= ~EOS_NET_CMD_FLAG_ONEW;
+ }
+
+ // before starting a transaction, set SPI peripheral to desired mode
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = ((cmd << 3) | (len >> 8)) & 0xFF;
+
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = len & 0xFF;
+
+ while ((r1 = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ while ((r2 = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+
+ if (cmd & EOS_NET_CMD_FLAG_ONEW) {
+ r1 = 0;
+ r2 = 0;
+ }
+
+ spi_buffer.cmd = ((r1 & 0xFF) >> 3);
+ spi_buffer.len_rx = ((r1 & 0x07) << 8);
+
+ spi_buffer.len_rx |= (r2 & 0xFF);
+ spi_buffer.len = MAX(len, spi_buffer.len_rx);
+
+ // Work around esp32 bug
+ if (spi_buffer.len < 6) {
+ spi_buffer.len = 6;
+ } else if ((spi_buffer.len + 2) % 4 != 0) {
+ spi_buffer.len = ((spi_buffer.len + 2)/4 + 1) * 4 - 2;
+ }
+
+ SPI1_REG(SPI_REG_TXCTRL) = SPI_TXWM(SPI_SIZE_CHUNK/2);
+ SPI1_REG(SPI_REG_RXCTRL) = SPI_RXWM(SPI_SIZE_CHUNK - 1);
+ SPI1_REG(SPI_REG_IE) = SPI_IP_TXWM | SPI_IP_RXWM;
+}
+
+static int spi_xchg_next(unsigned char *_buffer) {
+ unsigned char cmd;
+ unsigned char *buffer = NULL;
+ uint16_t len;
+
+ eos_msgq_pop(&spi_sndq, &cmd, &buffer, &len);
+ if (cmd) {
+ spi_xchg(cmd, buffer, len);
+ } else if (spi_rts) {
+ if (_buffer == NULL) _buffer = spi_bufq_pop();
+ if (_buffer) {
+ spi_xchg(0, _buffer, 0);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void spi_xchg_handler(void) {
+ int i;
+
+ if (SPI1_REG(SPI_REG_IP) & SPI_IP_TXWM) {
+ uint16_t sz_chunk = MIN(spi_buffer.len - spi_buffer.idx_tx, SPI_SIZE_CHUNK);
+ for (i=0; i<sz_chunk; i++) {
+ if (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL) break;
+ SPI1_REG(SPI_REG_TXFIFO) = spi_buffer.buffer[spi_buffer.idx_tx+i];
+ }
+ spi_buffer.idx_tx += i;
+ }
+
+ for (i=0; i<spi_buffer.idx_tx - spi_buffer.idx_rx; i++) {
+ volatile uint32_t x = SPI1_REG(SPI_REG_RXFIFO);
+ if (x & SPI_RXFIFO_EMPTY) break;
+ spi_buffer.buffer[spi_buffer.idx_rx+i] = x & 0xFF;
+ }
+ spi_buffer.idx_rx += i;
+
+ if (spi_buffer.idx_rx == spi_buffer.len) {
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+ SPI1_REG(SPI_REG_IE) = 0x0;
+ if (spi_buffer.cmd) {
+ int r = eos_evtq_push(spi_buffer.cmd | EOS_EVT_MASK_NET, spi_buffer.buffer, spi_buffer.len_rx);
+ if (r) spi_bufq_push(spi_buffer.buffer);
+ } else if ((spi_res || spi_res_next) && (spi_res_buf == NULL)) {
+ spi_res_buf = spi_buffer.buffer;
+ spi_res_next = 0;
+ } else {
+ spi_bufq_push(spi_buffer.buffer);
+ }
+ } else if (spi_buffer.idx_tx == spi_buffer.len) {
+ SPI1_REG(SPI_REG_RXCTRL) = SPI_RXWM(MIN(spi_buffer.len - spi_buffer.idx_rx - 1, SPI_SIZE_CHUNK - 1));
+ SPI1_REG(SPI_REG_IE) = SPI_IP_RXWM;
+ }
+}
+
+static void spi_cts_hanler(void) {
+ GPIO_REG(GPIO_RISE_IP) = (0x1 << SPI_GPIO_CTS_OFFSET);
+ spi_cts = 1;
+
+ if (spi_rdy) {
+ spi_xchg_next(NULL);
+ } else {
+ uint32_t iof_mask = ((uint32_t)1 << IOF_SPI1_SS2);
+ GPIO_REG(GPIO_IOF_EN) &= ~iof_mask;
+ }
+}
+
+static void spi_rts_hanler(void) {
+ uint32_t rts_offset = (0x1 << SPI_GPIO_RTS_OFFSET);
+ if (GPIO_REG(GPIO_RISE_IP) & rts_offset) {
+ GPIO_REG(GPIO_RISE_IP) = rts_offset;
+ spi_rts = 1;
+ if (spi_rdy && spi_cts) spi_reset();
+ }
+
+ if (GPIO_REG(GPIO_FALL_IP) & rts_offset) {
+ GPIO_REG(GPIO_FALL_IP) = rts_offset;
+ spi_rts = 0;
+ }
+}
+
+void eos_net_init(void) {
+ int i;
+
+ spi_bufq.idx_r = 0;
+ spi_bufq.idx_w = 0;
+ for (i=0; i<SPI_SIZE_BUFQ; i++) {
+ spi_bufq_push(spi_bufq_array[i]);
+ }
+ eos_msgq_init(&spi_sndq, spi_sndq_array, SPI_SIZE_BUFQ);
+ GPIO_REG(GPIO_IOF_SEL) &= ~SPI_IOF_MASK;
+ GPIO_REG(GPIO_IOF_EN) |= SPI_IOF_MASK;
+ eos_intr_set_handler(INT_SPI1_BASE, 5, spi_xchg_handler);
+
+ GPIO_REG(GPIO_OUTPUT_EN) &= ~(0x1 << SPI_GPIO_CTS_OFFSET);
+ GPIO_REG(GPIO_PULLUP_EN) |= (0x1 << SPI_GPIO_CTS_OFFSET);
+ GPIO_REG(GPIO_INPUT_EN) |= (0x1 << SPI_GPIO_CTS_OFFSET);
+ GPIO_REG(GPIO_RISE_IE) |= (0x1 << SPI_GPIO_CTS_OFFSET);
+ eos_intr_set_handler(INT_GPIO_BASE + SPI_GPIO_CTS_OFFSET, 5, spi_cts_hanler);
+
+ GPIO_REG(GPIO_OUTPUT_EN) &= ~(0x1 << SPI_GPIO_RTS_OFFSET);
+ GPIO_REG(GPIO_PULLUP_EN) |= (0x1 << SPI_GPIO_RTS_OFFSET);
+ GPIO_REG(GPIO_INPUT_EN) |= (0x1 << SPI_GPIO_RTS_OFFSET);
+ GPIO_REG(GPIO_RISE_IE) |= (0x1 << SPI_GPIO_RTS_OFFSET);
+ GPIO_REG(GPIO_FALL_IE) |= (0x1 << SPI_GPIO_RTS_OFFSET);
+ eos_intr_set_handler(INT_GPIO_BASE + SPI_GPIO_RTS_OFFSET, 5, spi_rts_hanler);
+}
+
+void eos_net_start(uint32_t sckdiv) {
+ uint32_t iof_mask = ((uint32_t)1 << IOF_SPI1_SS2);
+
+ GPIO_REG(GPIO_IOF_SEL) &= ~iof_mask;
+ GPIO_REG(GPIO_IOF_EN) |= iof_mask;
+
+ SPI1_REG(SPI_REG_SCKDIV) = sckdiv;
+ SPI1_REG(SPI_REG_SCKMODE) = SPI_MODE0;
+ SPI1_REG(SPI_REG_FMT) = SPI_FMT_PROTO(SPI_PROTO_S) |
+ SPI_FMT_ENDIAN(SPI_ENDIAN_MSB) |
+ SPI_FMT_DIR(SPI_DIR_RX) |
+ SPI_FMT_LEN(8);
+
+ // enable CS pin for selected channel/pin
+ SPI1_REG(SPI_REG_CSID) = 2;
+
+ // There is no way here to change the CS polarity.
+ // SPI1_REG(SPI_REG_CSDEF) = 0xFFFF;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ spi_rdy = 1;
+ if (spi_cts) spi_xchg_next(NULL);
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_net_stop(void) {
+ volatile uint8_t done = 0;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ spi_rdy = 0;
+ if (spi_cts) {
+ uint32_t iof_mask = ((uint32_t)1 << IOF_SPI1_SS2);
+ GPIO_REG(GPIO_IOF_EN) &= ~iof_mask;
+ done = 1;
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+
+ while (!done) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ done = spi_cts;
+ if (!done) asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+}
+
+int eos_net_reserve(unsigned char *buffer) {
+ int rv = EOS_OK;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ spi_res++;
+ if (spi_res_buf == NULL) {
+ spi_res_buf = buffer;
+ } else {
+ rv = spi_bufq_push(buffer);
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return rv;
+}
+
+int eos_net_acquire(unsigned char reserved) {
+ int ret = 0;
+
+ if (reserved) {
+ while (!ret) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (spi_res_buf) {
+ ret = 1;
+ } else {
+ asm volatile ("wfi");
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ } else {
+ clear_csr(mstatus, MSTATUS_MIE);
+ spi_res++;
+ spi_res_buf = spi_bufq_pop();
+ ret = (spi_res_buf != NULL);
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ return ret;
+}
+
+int eos_net_release(unsigned char reserved) {
+ int rv = EOS_OK;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (reserved) spi_res--;
+ if (!spi_res && spi_res_buf) {
+ rv = spi_bufq_push((unsigned char *)spi_res_buf);
+ if (!rv) spi_res_buf = NULL;
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return rv;
+}
+
+unsigned char *eos_net_alloc(void) {
+ volatile unsigned char *ret = NULL;
+
+ while (ret == NULL) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (spi_res_buf) {
+ ret = spi_res_buf;
+ spi_res_buf = NULL;
+ } else {
+ asm volatile ("wfi");
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+
+ return (unsigned char *)ret;
+}
+
+int eos_net_free(unsigned char *buffer, unsigned char reserve_next) {
+ int rv = EOS_OK;
+ uint8_t do_release = 1;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if ((spi_res || reserve_next) && (spi_res_buf == NULL)) {
+ spi_res_buf = buffer;
+ } else {
+ if (spi_rdy && spi_cts) do_release = spi_xchg_next(buffer);
+ if (do_release) rv = spi_bufq_push(buffer);
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return rv;
+}
+
+int eos_net_send(unsigned char cmd, unsigned char *buffer, uint16_t len) {
+ int rv = EOS_OK;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (spi_rdy && spi_cts) {
+ spi_xchg(cmd, buffer, len);
+ } else {
+ rv = eos_msgq_push(&spi_sndq, cmd, buffer, len);
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return rv;
+}