summaryrefslogtreecommitdiff
path: root/code/fe310/eos/spi.c
diff options
context:
space:
mode:
authorUros Majstorovic <majstor@majstor.org>2019-10-18 18:38:00 +0200
committerUros Majstorovic <majstor@majstor.org>2019-10-18 18:38:00 +0200
commit85500fe0d01b691a9bdd8c2330d26d66bc2bc177 (patch)
treec320c642ebb942d1b3692701d50c4a46e50e49f3 /code/fe310/eos/spi.c
parentf74dcae328e3650acf6b12499a55548b46117877 (diff)
added spi driver
Diffstat (limited to 'code/fe310/eos/spi.c')
-rw-r--r--code/fe310/eos/spi.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/code/fe310/eos/spi.c b/code/fe310/eos/spi.c
new file mode 100644
index 0000000..86a967d
--- /dev/null
+++ b/code/fe310/eos/spi.c
@@ -0,0 +1,284 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "eos.h"
+#include "msgq.h"
+#include "interrupt.h"
+#include "event.h"
+
+#include "net.h"
+#include "spi.h"
+#include "spi_def.h"
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+#define SPI_IOF_MASK (((uint32_t)1 << IOF_SPI1_SCK) | ((uint32_t)1 << IOF_SPI1_MOSI) | ((uint32_t)1 << IOF_SPI1_MISO)) | ((uint32_t)1 << IOF_SPI1_SS0) | ((uint32_t)1 << IOF_SPI1_SS2) | ((uint32_t)1 << IOF_SPI1_SS3)
+
+extern EOSMsgQ _eos_event_q;
+
+static uint8_t spi_dev;
+static uint8_t spi_dev_cs;
+static uint8_t spi_state_flags;
+
+uint32_t _eos_spi_state_len = 0;
+uint32_t _eos_spi_state_idx_tx = 0;
+uint32_t _eos_spi_state_idx_rx = 0;
+unsigned char *_eos_spi_state_buf = NULL;
+
+static eos_evt_fptr_t evt_handler[EOS_SPI_MAX_DEV];
+
+void spi_cs_set(void) {
+ /* cs low */
+ if (SPI1_REG(SPI_REG_CSMODE) == SPI_CSMODE_OFF) {
+ GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << spi_dev_cs);
+ } else {
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+ }
+ spi_state_flags &= ~SPI_FLAG_CS;
+}
+
+void spi_cs_clear(void) {
+ /* cs high */
+ if (SPI1_REG(SPI_REG_CSMODE) == SPI_CSMODE_OFF) {
+ GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << spi_dev_cs);
+ } else {
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+ }
+ spi_state_flags |= SPI_FLAG_CS;
+}
+
+static void spi_flush(void) {
+ SPI1_REG(SPI_REG_RXCTRL) = SPI_TXWM(1);
+ while (!(SPI1_REG(SPI_REG_IP) & SPI_IP_TXWM));
+ while (!(SPI1_REG(SPI_REG_RXFIFO) & SPI_RXFIFO_EMPTY));
+}
+
+static void spi_xchg_done(void) {
+ if (!(spi_state_flags & (EOS_SPI_FLAG_MORE | SPI_FLAG_CS))) spi_cs_clear();
+ SPI1_REG(SPI_REG_IE) = 0x0;
+ spi_state_flags &= ~SPI_FLAG_XCHG;
+}
+
+static void spi_handler_evt(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char idx = (type & ~EOS_EVT_MASK) - 1;
+ if (idx < EOS_SPI_MAX_DEV) {
+ evt_handler[idx](type, buffer, len);
+ } else {
+ eos_evtq_bad_handler(type, buffer, len);
+ }
+}
+
+void eos_spi_init(void) {
+ GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << spi_dev_cs);
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << SPI_CS_PIN_CAM);
+ GPIO_REG(GPIO_OUTPUT_EN) |= (1 << SPI_CS_PIN_CAM);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << SPI_CS_PIN_CAM);
+
+ 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);
+
+ GPIO_REG(GPIO_IOF_SEL) &= ~SPI_IOF_MASK;
+ GPIO_REG(GPIO_IOF_EN) |= SPI_IOF_MASK;
+
+ // There is no way here to change the CS polarity.
+ // SPI1_REG(SPI_REG_CSDEF) = 0xFFFF;
+
+ eos_intr_set(INT_SPI1_BASE, 5, NULL);
+ eos_evtq_set_handler(EOS_EVT_SPI, spi_handler_evt);
+ eos_spi_dev_release();
+}
+
+void eos_spi_dev_acquire(unsigned char dev) {
+ eos_net_stop();
+ spi_dev = dev;
+ spi_state_flags = 0;
+ switch (dev) {
+ case EOS_SPI_DEV_DISP:
+ SPI1_REG(SPI_REG_SCKDIV) = SPI_DIV_DISP;
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+ SPI1_REG(SPI_REG_CSID) = SPI_CS_IDX_DISP;
+ break;
+ case EOS_SPI_DEV_CARD:
+ SPI1_REG(SPI_REG_SCKDIV) = SPI_DIV_CARD;
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+ SPI1_REG(SPI_REG_CSID) = SPI_CS_IDX_CARD;
+ break;
+ case EOS_SPI_DEV_CAM:
+ spi_dev_cs = SPI_CS_PIN_CAM;
+ SPI1_REG(SPI_REG_SCKDIV) = SPI_DIV_CAM;
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_OFF;
+ SPI1_REG(SPI_REG_CSID) = SPI_CS_IDX_NONE;
+ break;
+ }
+ eos_intr_set_handler(INT_SPI1_BASE, eos_spi_xchg_handler);
+}
+
+void eos_spi_dev_release(void) {
+ eos_spi_xchg_wait();
+ if (spi_state_flags & EOS_SPI_FLAG_TX) spi_flush();
+ if (!(spi_state_flags & SPI_FLAG_CS)) spi_cs_clear();
+
+ spi_dev = EOS_SPI_DEV_NET;
+ eos_net_start();
+}
+
+void eos_spi_xchg(unsigned char *buffer, uint16_t len, uint8_t flags) {
+ if (!(flags & EOS_SPI_FLAG_TX) && (spi_state_flags & EOS_SPI_FLAG_TX)) spi_flush();
+
+ spi_state_flags &= 0xF0;
+ spi_state_flags |= (SPI_FLAG_XCHG | flags);
+ _eos_spi_state_buf = buffer;
+ _eos_spi_state_len = len;
+ _eos_spi_state_idx_tx = 0;
+ _eos_spi_state_idx_rx = 0;
+
+ if (spi_state_flags & SPI_FLAG_CS) spi_cs_set();
+ SPI1_REG(SPI_REG_TXCTRL) = SPI_TXWM(SPI_SIZE_WM);
+ SPI1_REG(SPI_REG_IE) = SPI_IP_TXWM;
+}
+
+void eos_spi_xchg_handler(void) {
+ int i;
+ uint16_t sz_chunk = MIN(_eos_spi_state_len - _eos_spi_state_idx_tx, SPI_SIZE_CHUNK);
+
+ for (i=0; i<sz_chunk; i++) {
+ volatile uint32_t x = SPI1_REG(SPI_REG_TXFIFO);
+ if (x & SPI_TXFIFO_FULL) break;
+ SPI1_REG(SPI_REG_TXFIFO) = _eos_spi_state_buf[_eos_spi_state_idx_tx+i];
+ }
+ _eos_spi_state_idx_tx += i;
+
+ for (i=0; i<_eos_spi_state_idx_tx - _eos_spi_state_idx_rx; i++) {
+ volatile uint32_t x = SPI1_REG(SPI_REG_RXFIFO);
+ if (x & SPI_RXFIFO_EMPTY) break;
+ _eos_spi_state_buf[_eos_spi_state_idx_rx+i] = x & 0xFF;
+ }
+ _eos_spi_state_idx_rx += i;
+
+ if ((spi_state_flags & EOS_SPI_FLAG_TX) && _eos_spi_state_idx_tx == _eos_spi_state_len) {
+ spi_state_flags &= ~EOS_SPI_FLAG_TX;
+ spi_xchg_done();
+ } else if (_eos_spi_state_idx_rx == _eos_spi_state_len) {
+ spi_xchg_done();
+ if (spi_dev == EOS_SPI_DEV_NET) {
+ eos_net_xchg_done();
+ } else {
+ eos_msgq_push(&_eos_event_q, EOS_EVT_SPI | spi_dev, _eos_spi_state_buf, _eos_spi_state_len);
+ }
+ } else if (_eos_spi_state_idx_tx == _eos_spi_state_len) {
+ SPI1_REG(SPI_REG_RXCTRL) = SPI_RXWM(MIN(_eos_spi_state_len - _eos_spi_state_idx_rx - 1, SPI_SIZE_WM - 1));
+ SPI1_REG(SPI_REG_IE) = SPI_IP_RXWM;
+ }
+}
+
+void eos_spi_xchg_wait(void) {
+ volatile uint8_t done = 0;
+
+ while (!done) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ done = !(spi_state_flags & SPI_FLAG_XCHG);
+ if (!done) asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+}
+
+uint8_t eos_spi_xchg8(uint8_t data, uint8_t flags) {
+ volatile uint32_t x = 0;
+ uint8_t rx = !(flags & EOS_SPI_FLAG_TX);
+
+ if (rx && (spi_state_flags & EOS_SPI_FLAG_TX)) spi_flush();
+
+ spi_state_flags &= 0xF0;
+ spi_state_flags |= flags;
+ if (spi_state_flags & SPI_FLAG_CS) spi_cs_set();
+
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = data;
+
+ if (rx) {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ }
+
+ if (!(spi_state_flags & (EOS_SPI_FLAG_MORE | SPI_FLAG_CS))) spi_cs_clear();
+
+ return x & 0xFF;
+}
+
+uint16_t eos_spi_xchg16(uint16_t data, uint8_t flags) {
+ volatile uint32_t x = 0;
+ uint8_t rx = !(flags & EOS_SPI_FLAG_TX);
+ uint16_t r;
+
+ if (rx && (spi_state_flags & EOS_SPI_FLAG_TX)) spi_flush();
+
+ spi_state_flags &= 0xF0;
+ spi_state_flags |= flags;
+ if (spi_state_flags & SPI_FLAG_CS) spi_cs_set();
+
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0xFF00) >> 8;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x00FF);
+
+ if (rx) {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r = (x & 0xFF) << 8;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= x & 0xFF;
+ }
+
+ if (!(spi_state_flags & (EOS_SPI_FLAG_MORE | SPI_FLAG_CS))) spi_cs_clear();
+
+ return x & 0xFF;
+}
+
+uint32_t eos_spi_xchg32(uint32_t data, uint8_t flags) {
+ volatile uint32_t x = 0;
+ uint8_t rx = !(flags & EOS_SPI_FLAG_TX);
+ uint32_t r;
+
+ if (rx && (spi_state_flags & EOS_SPI_FLAG_TX)) spi_flush();
+
+ spi_state_flags &= 0xF0;
+ spi_state_flags |= flags;
+ if (spi_state_flags & SPI_FLAG_CS) spi_cs_set();
+
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0xFF000000) >> 24;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x00FF0000) >> 16;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x0000FF00) >> 8;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x000000FF);
+
+ if (rx) {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r = (x & 0xFF) << 24;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 16;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 8;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= x & 0xFF;
+ }
+
+ if (!(spi_state_flags & (EOS_SPI_FLAG_MORE | SPI_FLAG_CS))) spi_cs_clear();
+
+ return r;
+}
+
+void eos_spi_set_handler(unsigned char dev, eos_evt_fptr_t handler) {
+ if (dev && (dev <= EOS_SPI_MAX_DEV)) {
+ dev--;
+ } else {
+ return;
+ }
+ evt_handler[dev] = handler;
+}