summaryrefslogtreecommitdiff
path: root/fw/fe310/eos/dev
diff options
context:
space:
mode:
Diffstat (limited to 'fw/fe310/eos/dev')
-rw-r--r--fw/fe310/eos/dev/Makefile20
-rw-r--r--fw/fe310/eos/dev/bq25895.c44
-rw-r--r--fw/fe310/eos/dev/bq25895.h5
-rw-r--r--fw/fe310/eos/dev/cam.c140
-rw-r--r--fw/fe310/eos/dev/cam.h88
-rw-r--r--fw/fe310/eos/dev/drv2605.h3
-rw-r--r--fw/fe310/eos/dev/eve.c152
-rw-r--r--fw/fe310/eos/dev/eve.h9
-rw-r--r--fw/fe310/eos/dev/lsm9ds1.h4
-rw-r--r--fw/fe310/eos/dev/net.c598
-rw-r--r--fw/fe310/eos/dev/net.h47
-rw-r--r--fw/fe310/eos/dev/ov2640.c861
-rw-r--r--fw/fe310/eos/dev/ov2640.h8
-rw-r--r--fw/fe310/eos/dev/ov2640_regs.h245
-rw-r--r--fw/fe310/eos/dev/sdc_crypto.c51
-rw-r--r--fw/fe310/eos/dev/sdc_crypto.h18
-rw-r--r--fw/fe310/eos/dev/sdcard.c539
-rw-r--r--fw/fe310/eos/dev/sdcard.h22
-rw-r--r--fw/fe310/eos/dev/spi.c113
-rw-r--r--fw/fe310/eos/dev/spi.h21
-rw-r--r--fw/fe310/eos/dev/spi_cfg.h52
21 files changed, 3040 insertions, 0 deletions
diff --git a/fw/fe310/eos/dev/Makefile b/fw/fe310/eos/dev/Makefile
new file mode 100644
index 0000000..7407212
--- /dev/null
+++ b/fw/fe310/eos/dev/Makefile
@@ -0,0 +1,20 @@
+include ../../common.mk
+CFLAGS += -I$(bsp_dir)/include -I$(ext_dir)/crypto
+
+obj = spi.o net.o bq25895.o sdcard.o sdc_crypto.o eve.o ov2640.o cam.o
+lib = ../../libeos-dev.a
+
+
+%.o: %.c %.h
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(lib)
+
+$(lib): $(obj)
+ $(AR) rcs $@ $(obj)
+
+clean:
+ rm -f *.o $(lib)
diff --git a/fw/fe310/eos/dev/bq25895.c b/fw/fe310/eos/dev/bq25895.c
new file mode 100644
index 0000000..11323c7
--- /dev/null
+++ b/fw/fe310/eos/dev/bq25895.c
@@ -0,0 +1,44 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "eos.h"
+#include "soc/pwr.h"
+#include "soc/i2c.h"
+
+#include "bq25895.h"
+
+static int reg_read(uint8_t reg, uint8_t *data) {
+ return eos_i2c_read8(BQ25895_ADDR, reg, data, 1);
+}
+
+static int reg_write(uint8_t reg, uint8_t data) {
+ return eos_i2c_write8(BQ25895_ADDR, reg, &data, 1);
+}
+
+int eos_bq25895_init(uint8_t wakeup_cause) {
+ int rst = (wakeup_cause == EOS_PWR_WAKE_RST);
+ int i, rv = EOS_OK;
+ uint8_t data = 0;
+
+ if (rst) {
+ rv = reg_write(0x14, 0x80); // reset
+ if (rv) printf("I2C ERROR 0x14\n");
+ rv = reg_write(0x14, 0x00); // disable watchdog
+ if (rv) printf("I2C ERROR 0x14\n");
+ rv = reg_write(0x07, 0x8d); // disable watchdog
+ if (rv) printf("I2C ERROR 0x07\n");
+ rv = reg_write(0x00, 0x28); // 2.1A input current
+ if (rv) printf("I2C ERROR 0x00\n");
+ rv = reg_write(0x02, 0x30); // enable ICO, disaable MaxCharge and D+/D-
+ if (rv) printf("I2C ERROR 0x02\n");
+ }
+
+ printf("BQ25895:\n");
+ for (i=0; i<0x15; i++) {
+ rv = reg_read(i, &data);
+ if (!rv) printf("REG%02x: %02x\n", i, data);
+ }
+
+ return EOS_OK;
+}
diff --git a/fw/fe310/eos/dev/bq25895.h b/fw/fe310/eos/dev/bq25895.h
new file mode 100644
index 0000000..cbef36e
--- /dev/null
+++ b/fw/fe310/eos/dev/bq25895.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+#define BQ25895_ADDR 0x6A
+
+int eos_bq25895_init(uint8_t wakeup_cause);
diff --git a/fw/fe310/eos/dev/cam.c b/fw/fe310/eos/dev/cam.c
new file mode 100644
index 0000000..50a0bdd
--- /dev/null
+++ b/fw/fe310/eos/dev/cam.c
@@ -0,0 +1,140 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "eos.h"
+
+#include "soc/spi.h"
+
+#include "cam.h"
+
+const int _eos_cam_resolution[][2] = {
+ {0, 0 },
+ // C/SIF Resolutions
+ {88, 72 }, /* QQCIF */
+ {176, 144 }, /* QCIF */
+ {352, 288 }, /* CIF */
+ {88, 60 }, /* QQSIF */
+ {176, 120 }, /* QSIF */
+ {352, 240 }, /* SIF */
+ // VGA Resolutions
+ {40, 30 }, /* QQQQVGA */
+ {80, 60 }, /* QQQVGA */
+ {160, 120 }, /* QQVGA */
+ {320, 240 }, /* QVGA */
+ {640, 480 }, /* VGA */
+ {60, 40 }, /* HQQQVGA */
+ {120, 80 }, /* HQQVGA */
+ {240, 160 }, /* HQVGA */
+ // FFT Resolutions
+ {64, 32 }, /* 64x32 */
+ {64, 64 }, /* 64x64 */
+ {128, 64 }, /* 128x64 */
+ {128, 128 }, /* 128x128 */
+ {320, 320 }, /* 128x128 */
+ // Other
+ {128, 160 }, /* LCD */
+ {128, 160 }, /* QQVGA2 */
+ {720, 480 }, /* WVGA */
+ {752, 480 }, /* WVGA2 */
+ {800, 600 }, /* SVGA */
+ {1024, 768 }, /* XGA */
+ {1280, 1024}, /* SXGA */
+ {1600, 1200}, /* UXGA */
+ {1280, 720 }, /* HD */
+ {1920, 1080}, /* FHD */
+ {2560, 1440}, /* QHD */
+ {2048, 1536}, /* QXGA */
+ {2560, 1600}, /* WQXGA */
+ {2592, 1944}, /* WQXGA2 */
+};
+
+#define CAM_REG_CAPTURE_CTRL 0x01
+#define CAM_REG_FIFO_CTRL 0x04
+
+#define CAM_REG_GPIO_DIR 0x05
+#define CAM_REG_GPIO_WR 0x06
+#define CAM_REG_GPIO_RD 0x45
+
+#define CAM_REG_STATUS 0x41
+
+#define CAM_REG_VER 0x40
+
+#define CAM_REG_READ_BURST 0x3c
+#define CAM_REG_READ_BYTE 0x3d
+
+#define CAM_REG_FIFO_SIZE1 0x42
+#define CAM_REG_FIFO_SIZE2 0x43
+#define CAM_REG_FIFO_SIZE3 0x44
+
+#define CAM_VAL_FIFO_CTRL_CLEAR 0x01
+#define CAM_VAL_FIFO_CTRL_START 0x02
+#define CAM_VAL_FIFO_CTRL_RSTWR 0x10
+#define CAM_VAL_FIFO_CTRL_RSTRD 0x20
+
+#define CAM_VAL_STATUS_VSYNC 0x01
+#define CAM_VAL_STATUS_SHUTTER 0x02
+#define CAM_VAL_STATUS_CPTDONE 0x08
+
+#define CAM_VAL_GPIO_RST 0x01
+#define CAM_VAL_GPIO_PWRDN 0x02
+#define CAM_VAL_GPIO_PWREN 0x04
+
+static uint8_t reg_read(uint8_t addr) {
+ uint8_t ret;
+
+ eos_spi_cs_set();
+ eos_spi_xchg8(addr, 0);
+ ret = eos_spi_xchg8(0, 0);
+ eos_spi_cs_clear();
+
+ return ret;
+}
+
+static void reg_write(uint8_t addr, uint8_t val) {
+ eos_spi_cs_set();
+ eos_spi_xchg8(addr | 0x80, 0);
+ eos_spi_xchg8(val, 0);
+ eos_spi_cs_clear();
+}
+
+void eos_cam_capture(void) {
+ reg_write(CAM_REG_FIFO_CTRL, CAM_VAL_FIFO_CTRL_START);
+}
+
+int eos_cam_capture_done(void) {
+ return !!(reg_read(CAM_REG_STATUS) & CAM_VAL_STATUS_CPTDONE);
+}
+
+void eos_cam_capture_wait(void) {
+ int done = 0;
+
+ while (!done) {
+ done = eos_cam_capture_done();
+ }
+}
+
+uint32_t eos_cam_fbuf_size(void) {
+ uint32_t ret;
+
+ ret = reg_read(CAM_REG_FIFO_SIZE1);
+ ret |= reg_read(CAM_REG_FIFO_SIZE2) << 8;
+ ret |= (reg_read(CAM_REG_FIFO_SIZE3) & 0x7f) << 16;
+ return ret;
+}
+
+void eos_cam_fbuf_read(uint8_t *buffer, uint16_t sz, int first) {
+ int i;
+
+ eos_spi_cs_set();
+ eos_spi_xchg8(CAM_REG_READ_BURST, 0);
+ if (first) eos_spi_xchg8(0, 0);
+
+ for (i=0; i<sz; i++) {
+ buffer[i] = eos_spi_xchg8(0, 0);
+ }
+ eos_spi_cs_clear();
+}
+
+void eos_cam_fbuf_done(void) {
+ reg_write(CAM_REG_FIFO_CTRL, CAM_VAL_FIFO_CTRL_CLEAR);
+}
diff --git a/fw/fe310/eos/dev/cam.h b/fw/fe310/eos/dev/cam.h
new file mode 100644
index 0000000..f61757b
--- /dev/null
+++ b/fw/fe310/eos/dev/cam.h
@@ -0,0 +1,88 @@
+#include <stdint.h>
+
+typedef enum {
+ PIXFORMAT_INVALID = 0,
+ PIXFORMAT_BINARY, // 1BPP/BINARY
+ PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
+ PIXFORMAT_RGB565, // 2BPP/RGB565
+ PIXFORMAT_YUV422, // 2BPP/YUV422
+ PIXFORMAT_BAYER, // 1BPP/RAW
+ PIXFORMAT_JPEG, // JPEG/COMPRESSED
+} pixformat_t;
+
+typedef enum {
+ FRAMESIZE_INVALID = 0,
+ // C/SIF Resolutions
+ FRAMESIZE_QQCIF, // 88x72
+ FRAMESIZE_QCIF, // 176x144
+ FRAMESIZE_CIF, // 352x288
+ FRAMESIZE_QQSIF, // 88x60
+ FRAMESIZE_QSIF, // 176x120
+ FRAMESIZE_SIF, // 352x240
+ // VGA Resolutions
+ FRAMESIZE_QQQQVGA, // 40x30
+ FRAMESIZE_QQQVGA, // 80x60
+ FRAMESIZE_QQVGA, // 160x120
+ FRAMESIZE_QVGA, // 320x240
+ FRAMESIZE_VGA, // 640x480
+ FRAMESIZE_HQQQVGA, // 60x40
+ FRAMESIZE_HQQVGA, // 120x80
+ FRAMESIZE_HQVGA, // 240x160
+ // FFT Resolutions
+ FRAMESIZE_64X32, // 64x32
+ FRAMESIZE_64X64, // 64x64
+ FRAMESIZE_128X64, // 128x64
+ FRAMESIZE_128X128, // 128x128
+ FRAMESIZE_320X320, // 320x320
+ // Other
+ FRAMESIZE_LCD, // 128x160
+ FRAMESIZE_QQVGA2, // 128x160
+ FRAMESIZE_WVGA, // 720x480
+ FRAMESIZE_WVGA2, // 752x480
+ FRAMESIZE_SVGA, // 800x600
+ FRAMESIZE_XGA, // 1024x768
+ FRAMESIZE_SXGA, // 1280x1024
+ FRAMESIZE_UXGA, // 1600x1200
+ FRAMESIZE_HD, // 1280x720
+ FRAMESIZE_FHD, // 1920x1080
+ FRAMESIZE_QHD, // 2560x1440
+ FRAMESIZE_QXGA, // 2048x1536
+ FRAMESIZE_WQXGA, // 2560x1600
+ FRAMESIZE_WQXGA2, // 2592x1944
+} framesize_t;
+
+typedef enum {
+ GAINCEILING_2X,
+ GAINCEILING_4X,
+ GAINCEILING_8X,
+ GAINCEILING_16X,
+ GAINCEILING_32X,
+ GAINCEILING_64X,
+ GAINCEILING_128X,
+} gainceiling_t;
+
+typedef enum {
+ SDE_NORMAL,
+ SDE_NEGATIVE,
+} sde_t;
+
+extern const int _eos_cam_resolution[][2];
+
+#define IM_LOG2_2(x) (((x) & 0x2ULL) ? ( 2 ) : 1) // NO ({ ... }) !
+#define IM_LOG2_4(x) (((x) & 0xCULL) ? ( 2 + IM_LOG2_2((x) >> 2)) : IM_LOG2_2(x)) // NO ({ ... }) !
+#define IM_LOG2_8(x) (((x) & 0xF0ULL) ? ( 4 + IM_LOG2_4((x) >> 4)) : IM_LOG2_4(x)) // NO ({ ... }) !
+#define IM_LOG2_16(x) (((x) & 0xFF00ULL) ? ( 8 + IM_LOG2_8((x) >> 8)) : IM_LOG2_8(x)) // NO ({ ... }) !
+#define IM_LOG2_32(x) (((x) & 0xFFFF0000ULL) ? (16 + IM_LOG2_16((x) >> 16)) : IM_LOG2_16(x)) // NO ({ ... }) !
+#define IM_LOG2(x) (((x) & 0xFFFFFFFF00000000ULL) ? (32 + IM_LOG2_32((x) >> 32)) : IM_LOG2_32(x)) // NO ({ ... }) !
+
+#define IM_MAX(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
+#define IM_MIN(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
+#define IM_DIV(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _b ? (_a / _b) : 0; })
+#define IM_MOD(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _b ? (_a % _b) : 0; })
+
+void eos_cam_capture(void);
+int eos_cam_capture_done(void);
+void eos_cam_capture_wait(void);
+uint32_t eos_cam_fbuf_size(void);
+void eos_cam_fbuf_read(uint8_t *buffer, uint16_t sz, int first);
+void eos_cam_fbuf_done(void);
diff --git a/fw/fe310/eos/dev/drv2605.h b/fw/fe310/eos/dev/drv2605.h
new file mode 100644
index 0000000..fe90a9b
--- /dev/null
+++ b/fw/fe310/eos/dev/drv2605.h
@@ -0,0 +1,3 @@
+#include <stdint.h>
+
+#define DRV2605_ADDR 0x5A
diff --git a/fw/fe310/eos/dev/eve.c b/fw/fe310/eos/dev/eve.c
new file mode 100644
index 0000000..0f98fed
--- /dev/null
+++ b/fw/fe310/eos/dev/eve.c
@@ -0,0 +1,152 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "platform.h"
+
+#include "eos.h"
+#include "event.h"
+
+#include "board.h"
+
+#include "soc/interrupt.h"
+#include "soc/pwr.h"
+#include "soc/i2s.h"
+
+#include "eve/eve.h"
+#include "eve/eve_touch_engine.h"
+
+#include "eve.h"
+
+static void handle_time(unsigned char type) {
+ if (!eos_eve_running()) return;
+
+ eve_spi_start();
+ eve_handle_time();
+ eve_spi_stop();
+}
+
+static void handle_evt(unsigned char type, unsigned char *buffer, uint16_t len) {
+ if (!eos_eve_running()) return;
+
+ eve_spi_start();
+ eve_handle_intr();
+ eve_spi_stop();
+
+ GPIO_REG(GPIO_LOW_IE) |= (1 << EVE_PIN_INTR);
+}
+
+static void handle_intr(void) {
+ GPIO_REG(GPIO_LOW_IE) &= ~(1 << EVE_PIN_INTR);
+ GPIO_REG(GPIO_LOW_IP) = (1 << EVE_PIN_INTR);
+ eos_evtq_push_isr(EOS_EVT_EVE | EVE_ETYPE_INTR, NULL, 0);
+}
+
+static void _start(void) {
+ eve_touch_start();
+ eve_start();
+
+ GPIO_REG(GPIO_INPUT_EN) |= (1 << EVE_PIN_INTR);
+ GPIO_REG(GPIO_LOW_IE) |= (1 << EVE_PIN_INTR);
+
+ eos_intr_enable(INT_GPIO_BASE + EVE_PIN_INTR);
+}
+
+static void _stop(void) {
+ eos_intr_disable(INT_GPIO_BASE + EVE_PIN_INTR);
+
+ GPIO_REG(GPIO_LOW_IE) &= ~(1 << EVE_PIN_INTR);
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << EVE_PIN_INTR);
+
+ eve_touch_stop();
+ eve_stop();
+}
+
+int eos_eve_init(uint8_t wakeup_cause) {
+ int rst = (wakeup_cause == EOS_PWR_WAKE_RST);
+ int rv = EVE_OK;
+
+ eve_spi_start();
+ if (rst) {
+ rv = eve_init();
+ if (!rv) {
+ eve_gpio_set_dir(EVE_GPIO_DIR);
+ eve_touch_init_engine();
+ }
+ } else {
+ eve_activate();
+ }
+ eve_spi_stop();
+
+ if (rv) return EOS_ERR;
+
+ eve_touch_init();
+
+ eos_evtq_set_handler(EOS_EVT_EVE, handle_evt);
+ eos_timer_set_handler(EOS_TIMER_ETYPE_UI, handle_time);
+ eos_intr_set_handler(INT_GPIO_BASE + EVE_PIN_INTR, handle_intr);
+ eos_intr_set_priority(INT_GPIO_BASE + EVE_PIN_INTR, IRQ_PRIORITY_EVE);
+
+ return EOS_OK;
+}
+
+void eos_eve_calibrate(void) {
+ uint32_t matrix[6];
+ int r;
+
+ eve_spi_start();
+
+ eve_brightness(0x40);
+ eve_touch_set_extended(0);
+
+ eve_cmd(CMD_TEXT, "hhhhs", EVE_HSIZE/2, EVE_VSIZE/2, 27, EVE_OPT_CENTER, "Please tap on the dot.");
+ eve_cmd(CMD_CALIBRATE, "w", 0);
+ eve_cmd_exec(0);
+
+ do {
+ r = eve_cmd_done();
+ if (r < 0) break;
+ eve_spi_stop();
+ eos_evtq_exec();
+ eve_spi_start();
+ } while (!r);
+
+ eve_touch_set_extended(1);
+ eve_brightness(0);
+
+ eve_touch_get_matrix(matrix);
+ eve_spi_stop();
+
+ printf("TOUCH MATRIX:\n");
+ printf("uint32_t touch_matrix[6] = {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x}\n", matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+}
+
+void eos_eve_set_touch_matrix(const uint32_t *matrix) {
+ eve_spi_start();
+ eve_touch_set_matrix(matrix);
+ eve_spi_stop();
+}
+
+int eos_eve_run(uint8_t wakeup_cause) {
+ eve_spi_start();
+ _start();
+ eve_start_clk();
+ eve_spi_stop();
+
+ return EOS_OK;
+}
+
+void eos_eve_start(void) {
+ eve_spi_start();
+ _start();
+ eve_spi_stop();
+}
+
+void eos_eve_stop(void) {
+ eve_spi_start();
+ _stop();
+ eve_spi_stop();
+}
+
+int eos_eve_running(void) {
+ return !!(GPIO_REG(GPIO_INPUT_EN) & (1 << EVE_PIN_INTR));
+}
diff --git a/fw/fe310/eos/dev/eve.h b/fw/fe310/eos/dev/eve.h
new file mode 100644
index 0000000..8eb982f
--- /dev/null
+++ b/fw/fe310/eos/dev/eve.h
@@ -0,0 +1,9 @@
+#include <stdint.h>
+
+int eos_eve_init(uint8_t wakeup_cause);
+void eos_eve_calibrate(void);
+void eos_eve_set_touch_matrix(const uint32_t *matrix);
+int eos_eve_run(uint8_t wakeup_cause);
+void eos_eve_start(void);
+void eos_eve_stop(void);
+int eos_eve_running(void);
diff --git a/fw/fe310/eos/dev/lsm9ds1.h b/fw/fe310/eos/dev/lsm9ds1.h
new file mode 100644
index 0000000..92220e7
--- /dev/null
+++ b/fw/fe310/eos/dev/lsm9ds1.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+
+#define LSM9DS1_ADDR_AG 0x1E
+#define LSM9DS1_ADDR_M 0x6B
diff --git a/fw/fe310/eos/dev/net.c b/fw/fe310/eos/dev/net.c
new file mode 100644
index 0000000..d287e5a
--- /dev/null
+++ b/fw/fe310/eos/dev/net.c
@@ -0,0 +1,598 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "eos.h"
+#include "msgq.h"
+#include "event.h"
+
+#include "board.h"
+
+#include "soc/interrupt.h"
+#include "soc/timer.h"
+#include "soc/pwr.h"
+#include "soc/spi.h"
+#include "soc/spi_priv.h"
+
+#include "spi.h"
+
+#include "net.h"
+
+#define NET_SIZE_HDR 3
+#define NET_STATE_FLAG_RUN 0x01
+#define NET_STATE_FLAG_INIT 0x02
+#define NET_STATE_FLAG_XCHG 0x04
+#define NET_STATE_FLAG_ONEW 0x10
+#define NET_STATE_FLAG_SYNC 0x20
+#define NET_STATE_FLAG_RTS 0x40
+#define NET_STATE_FLAG_CTS 0x80
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+static EOSBufQ net_buf_q;
+static unsigned char *net_bufq_array[EOS_NET_SIZE_BUFQ];
+static unsigned char net_bufq_buffer[EOS_NET_SIZE_BUFQ][EOS_NET_SIZE_BUF];
+
+static EOSMsgQ net_send_q;
+static EOSMsgItem net_sndq_array[EOS_NET_SIZE_BUFQ];
+
+static volatile uint8_t net_state_flags = 0;
+static unsigned char net_state_type = 0;
+static uint32_t net_state_len_tx = 0;
+static uint32_t net_state_len_rx = 0;
+unsigned char *net_state_buf = NULL;
+
+static uint8_t net_state_next_cnt = 0;
+static unsigned char *net_state_next_buf = NULL;
+
+static eos_evt_handler_t net_handler[EOS_NET_MAX_MTYPE];
+static uint16_t net_wrapper_acq[EOS_EVT_MAX_EVT];
+static uint16_t net_flags_acq[EOS_EVT_MAX_EVT];
+
+static int net_xchg_sleep(void) {
+ int i;
+ int rv = EOS_OK;
+ volatile uint32_t x = 0;
+ net_state_flags &= ~NET_STATE_FLAG_CTS;
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+
+ SPI1_REG(SPI_REG_TXFIFO) = 0xFF;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ if (x & 0xFF) rv = EOS_ERR_BUSY;
+
+ for (i=0; i<7; i++) {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = 0;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ }
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+
+ return rv;
+}
+
+static void net_xchg_wake(void) {
+ int i;
+ volatile uint32_t x = 0;
+ net_state_flags &= ~NET_STATE_FLAG_CTS;
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+
+ for (i=0; i<8; i++) {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = 0;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ }
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+}
+
+static void net_xchg_reset(void) {
+ volatile uint32_t x = 0;
+ net_state_flags &= ~NET_STATE_FLAG_CTS;
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+
+ SPI1_REG(SPI_REG_TXFIFO) = 0;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+}
+
+static void net_xchg_start(unsigned char type, unsigned char *buffer, uint16_t len) {
+ net_state_flags &= ~NET_STATE_FLAG_CTS;
+ net_state_flags |= (NET_STATE_FLAG_INIT | NET_STATE_FLAG_XCHG);
+
+ if (net_state_next_cnt && (net_state_next_buf == NULL)) type |= EOS_NET_MTYPE_FLAG_ONEW;
+ if (type & EOS_NET_MTYPE_FLAG_ONEW) net_state_flags |= NET_STATE_FLAG_ONEW;
+
+ net_state_type = type;
+ net_state_len_tx = len;
+ net_state_len_rx = 0;
+ net_state_buf = buffer;
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+ SPI1_REG(SPI_REG_TXFIFO) = type;
+ SPI1_REG(SPI_REG_TXFIFO) = (len >> 8) & 0xFF;
+ SPI1_REG(SPI_REG_TXFIFO) = (len & 0xFF);
+ SPI1_REG(SPI_REG_RXCTRL) = SPI_RXWM(2);
+ SPI1_REG(SPI_REG_IE) = SPI_IP_RXWM;
+}
+
+static int net_xchg_next(unsigned char *_buffer) {
+ unsigned char type;
+ unsigned char *buffer = NULL;
+ uint16_t len;
+ int ret = _buffer ? 1 : 0;
+
+ eos_msgq_pop(&net_send_q, &type, &buffer, &len);
+ if (type) {
+ net_xchg_start(type, buffer, len);
+ } else if (net_state_flags & NET_STATE_FLAG_RTS) {
+ if (_buffer) {
+ buffer = _buffer;
+ ret = 0;
+ } else {
+ buffer = eos_bufq_pop(&net_buf_q);
+ }
+ if (buffer) net_xchg_start(0, buffer, 0);
+ }
+
+ return ret;
+}
+
+static void net_handle_xchg(void) {
+ volatile uint32_t r1, r2, r3;
+ uint32_t len;
+
+ if (net_state_flags & NET_STATE_FLAG_INIT) {
+ net_state_flags &= ~NET_STATE_FLAG_INIT;
+
+ r1 = SPI1_REG(SPI_REG_RXFIFO);
+ r2 = SPI1_REG(SPI_REG_RXFIFO);
+ r3 = SPI1_REG(SPI_REG_RXFIFO);
+
+ if (net_state_flags & NET_STATE_FLAG_ONEW) {
+ r1 = 0;
+ r2 = 0;
+ r3 = 0;
+ }
+
+ net_state_type = (r1 & 0xFF);
+ net_state_len_rx = (r2 & 0xFF) << 8;
+ net_state_len_rx |= (r3 & 0xFF);
+ len = MAX(net_state_len_tx, net_state_len_rx);
+
+ if (len > EOS_NET_MTU) {
+ net_state_flags &= ~NET_STATE_FLAG_XCHG;
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+ SPI1_REG(SPI_REG_IE) = 0x0;
+ return;
+ }
+
+ // esp32 dma workaraund
+ if (len < 8 - NET_SIZE_HDR) {
+ len = 8 - NET_SIZE_HDR;
+ } else if ((len + NET_SIZE_HDR) % 4 != 0) {
+ len = ((len + NET_SIZE_HDR)/4 + 1) * 4 - NET_SIZE_HDR;
+ }
+
+ _eos_spi_xchg_init(net_state_buf, len, 0);
+ SPI1_REG(SPI_REG_TXCTRL) = SPI_TXWM(SPI_SIZE_WM);
+ SPI1_REG(SPI_REG_IE) = SPI_IP_TXWM;
+ return;
+ }
+
+ eos_spi_handle_xchg();
+ if (SPI1_REG(SPI_REG_CSMODE) == SPI_CSMODE_AUTO) { // exchange done
+ if (!(net_state_flags & NET_STATE_FLAG_SYNC)) {
+ if (net_state_type) {
+ int r = eos_evtq_push_isr(EOS_EVT_NET | net_state_type, net_state_buf, net_state_len_rx);
+ if (r) eos_bufq_push(&net_buf_q, net_state_buf);
+ } else if (((net_state_flags & NET_STATE_FLAG_ONEW) || net_state_next_cnt) && (net_state_next_buf == NULL)) {
+ net_state_next_buf = net_state_buf;
+ } else {
+ eos_bufq_push(&net_buf_q, net_state_buf);
+ }
+ }
+ net_state_flags &= ~(NET_STATE_FLAG_ONEW | NET_STATE_FLAG_XCHG);
+ }
+}
+
+static void net_handle_cts(void) {
+ GPIO_REG(GPIO_RISE_IP) = (1 << NET_PIN_CTS);
+ net_state_flags |= NET_STATE_FLAG_CTS;
+
+ if (net_state_flags & NET_STATE_FLAG_RUN) {
+ net_xchg_next(NULL);
+ }
+}
+
+static void net_handle_rts(void) {
+ uint32_t rts_offset = (1 << NET_PIN_RTS);
+
+ if (GPIO_REG(GPIO_RISE_IP) & rts_offset) {
+ GPIO_REG(GPIO_RISE_IP) = rts_offset;
+ net_state_flags |= NET_STATE_FLAG_RTS;
+ if ((net_state_flags & NET_STATE_FLAG_RUN) && (net_state_flags & NET_STATE_FLAG_CTS)) {
+ net_xchg_reset();
+ }
+ } else if (GPIO_REG(GPIO_FALL_IP) & rts_offset) {
+ GPIO_REG(GPIO_FALL_IP) = rts_offset;
+ net_state_flags &= ~NET_STATE_FLAG_RTS;
+ }
+}
+
+static void net_handle_evt(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char idx = (type & ~EOS_EVT_MASK) - 1;
+
+ if (idx < EOS_NET_MAX_MTYPE) {
+ net_handler[idx](type, buffer, len);
+ } else {
+ eos_net_bad_handler(type, buffer, len);
+ }
+}
+
+static int net_acquire(unsigned char reserved) {
+ int ret = 0;
+
+ if (reserved) {
+ while (!ret) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (net_state_next_buf) {
+ ret = 1;
+ net_state_next_cnt--;
+ } else {
+ asm volatile ("wfi");
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ } else {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (net_state_next_buf == NULL) net_state_next_buf = eos_bufq_pop(&net_buf_q);
+ ret = (net_state_next_buf != NULL);
+ if (!ret) net_state_next_cnt++;
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ return ret;
+}
+
+static void evt_handler_wrapper(unsigned char type, unsigned char *buffer, uint16_t len, unsigned char idx, uint16_t flag) {
+ int ok;
+
+ ok = net_acquire(net_wrapper_acq[idx] & flag);
+ if (ok) {
+ eos_evtq_get_handler(type)(type, buffer, len);
+ eos_net_release();
+ net_wrapper_acq[idx] &= ~flag;
+ } else {
+ net_wrapper_acq[idx] |= flag;
+ eos_evtq_push(type, buffer, len);
+ }
+}
+
+static void evt_handler(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char idx = (type & EOS_EVT_MASK) >> 4;
+
+ if (idx && (idx <= EOS_EVT_MAX_EVT)) {
+ uint16_t flag = (uint16_t)1 << (type & ~EOS_EVT_MASK);
+
+ idx--;
+ if (flag & net_flags_acq[idx]) {
+ evt_handler_wrapper(type, buffer, len, idx, flag);
+ } else {
+ eos_evtq_get_handler(type)(type, buffer, len);
+ }
+ } else {
+ eos_evtq_bad_handler(type, buffer, len);
+ }
+}
+
+static void net_pause(void) {
+ net_state_flags &= ~NET_STATE_FLAG_RUN;
+}
+
+static void net_resume(void) {
+ net_state_flags |= NET_STATE_FLAG_RUN;
+ if (net_state_flags & NET_STATE_FLAG_CTS) {
+ net_xchg_next(NULL);
+ }
+}
+
+static void net_start(void) {
+ eos_spi_dev_configure(EOS_SPI_DEV_NET);
+ eos_intr_set_handler(INT_SPI1_BASE, net_handle_xchg);
+}
+
+static void net_stop(void) {
+ eos_intr_set_handler(INT_SPI1_BASE, NULL);
+}
+
+int eos_net_init(uint8_t wakeup_cause) {
+ int i;
+
+ eos_msgq_init(&net_send_q, net_sndq_array, EOS_NET_SIZE_BUFQ);
+ eos_bufq_init(&net_buf_q, net_bufq_array, EOS_NET_SIZE_BUFQ);
+ for (i=0; i<EOS_NET_SIZE_BUFQ; i++) {
+ eos_bufq_push(&net_buf_q, net_bufq_buffer[i]);
+ }
+
+ for (i=0; i<EOS_NET_MAX_MTYPE; i++) {
+ net_handler[i] = eos_net_bad_handler;
+ }
+ eos_evtq_set_handler(0, evt_handler);
+ eos_evtq_set_handler(EOS_EVT_NET, net_handle_evt);
+
+ GPIO_REG(GPIO_INPUT_EN) |= (1 << NET_PIN_CTS);
+ GPIO_REG(GPIO_RISE_IE) |= (1 << NET_PIN_CTS);
+ eos_intr_set(INT_GPIO_BASE + NET_PIN_CTS, IRQ_PRIORITY_NET_CTS, net_handle_cts);
+
+ GPIO_REG(GPIO_INPUT_EN) |= (1 << NET_PIN_RTS);
+ GPIO_REG(GPIO_RISE_IE) |= (1 << NET_PIN_RTS);
+ GPIO_REG(GPIO_FALL_IE) |= (1 << NET_PIN_RTS);
+ eos_intr_set(INT_GPIO_BASE + NET_PIN_RTS, IRQ_PRIORITY_NET_RTS, net_handle_rts);
+
+ /* set initial state */
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (GPIO_REG(GPIO_INPUT_VAL) & (1 << NET_PIN_CTS)) net_state_flags |= NET_STATE_FLAG_CTS;
+ if (GPIO_REG(GPIO_INPUT_VAL) & (1 << NET_PIN_RTS)) net_state_flags |= NET_STATE_FLAG_RTS;
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return EOS_OK;
+}
+
+int eos_net_run(uint8_t wakeup_cause) {
+ net_start();
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (wakeup_cause != EOS_PWR_WAKE_RST) {
+ if (wakeup_cause != EOS_PWR_WAKE_BTN) {
+ net_xchg_wake();
+ }
+ if (!(net_state_flags & NET_STATE_FLAG_CTS)) {
+ while (!(GPIO_REG(GPIO_RISE_IP) & (1 << NET_PIN_CTS))) {
+ asm volatile ("wfi");
+ }
+ GPIO_REG(GPIO_RISE_IP) = (1 << NET_PIN_CTS);
+ }
+ net_xchg_reset();
+ }
+ net_resume();
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return EOS_OK;
+}
+
+void eos_net_start(void) {
+ net_start();
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ net_resume();
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_net_stop(void) {
+ uint8_t done = 0;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (net_state_flags & NET_STATE_FLAG_RUN) {
+ net_state_flags &= ~NET_STATE_FLAG_RUN;
+ done = !(net_state_flags & NET_STATE_FLAG_XCHG);
+ } else {
+ done = 1;
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+
+ while (!done) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ done = !(net_state_flags & NET_STATE_FLAG_XCHG);
+ if (!done) asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ net_stop();
+}
+
+int eos_net_sleep(uint32_t timeout) {
+ volatile uint64_t *mtime = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIME);
+ uint64_t then_ms = timeout + *mtime * 1000 / EOS_TIMER_RTC_FREQ;
+ uint8_t done = 0;
+ int rv = EOS_OK;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (!(net_state_flags & NET_STATE_FLAG_RUN)) rv = EOS_ERR;
+ set_csr(mstatus, MSTATUS_MIE);
+
+ if (rv) return rv;
+
+ do {
+ if (*mtime * 1000 / EOS_TIMER_RTC_FREQ > then_ms) return EOS_ERR_TIMEOUT;
+ clear_csr(mstatus, MSTATUS_MIE);
+ eos_evtq_flush_isr();
+ done = (eos_msgq_len(&net_send_q) == 0);
+ done = done && (!(net_state_flags & NET_STATE_FLAG_RTS) && (net_state_flags & NET_STATE_FLAG_CTS));
+ if (done) done = (net_xchg_sleep() == EOS_OK);
+ if (!done) {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ } while (!done);
+
+ while (!(GPIO_REG(GPIO_RISE_IP) & (1 << NET_PIN_CTS))) {
+ if (*mtime * 1000 / EOS_TIMER_RTC_FREQ > then_ms) {
+ rv = EOS_ERR_TIMEOUT;
+ break;
+ }
+ asm volatile ("wfi");
+ }
+
+ if (!rv) {
+ GPIO_REG(GPIO_RISE_IP) = (1 << NET_PIN_CTS);
+ net_state_flags &= ~NET_STATE_FLAG_RUN;
+ }
+
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return rv;
+}
+
+void eos_net_bad_handler(unsigned char type, unsigned char *buffer, uint16_t len) {
+ eos_evtq_bad_handler(type, buffer, len);
+ if (buffer) eos_net_free(buffer, 0);
+}
+
+void eos_net_set_handler(unsigned char mtype, eos_evt_handler_t handler) {
+ if (handler == NULL) handler = eos_net_bad_handler;
+ if (mtype && (mtype <= EOS_NET_MAX_MTYPE)) net_handler[mtype - 1] = handler;
+}
+
+void eos_net_acquire_for_evt(unsigned char type, char acq) {
+ unsigned char idx = (type & EOS_EVT_MASK) >> 4;
+ uint16_t flag = type & ~EOS_EVT_MASK ? (uint16_t)1 << (type & ~EOS_EVT_MASK) : 0xFFFF;
+
+ if (idx && (idx <= EOS_EVT_MAX_EVT)) {
+ idx--;
+ net_flags_acq[idx] &= ~flag;
+ if (acq) net_flags_acq[idx] |= flag;
+ }
+}
+
+void eos_net_acquire(void) {
+ unsigned char acq = net_acquire(0);
+ if (!acq) net_acquire(1);
+}
+
+void eos_net_release(void) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (!net_state_next_cnt && net_state_next_buf) {
+ eos_bufq_push(&net_buf_q, net_state_next_buf);
+ net_state_next_buf = NULL;
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+unsigned char *eos_net_alloc(void) {
+ unsigned char *ret = NULL;
+
+ while (!ret) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (net_state_next_buf) {
+ ret = net_state_next_buf;
+ net_state_next_buf = NULL;
+ } else {
+ asm volatile ("wfi");
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+
+ return ret;
+}
+
+void eos_net_free(unsigned char *buffer, unsigned char more) {
+ uint8_t do_release = 1;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if ((more || net_state_next_cnt) && (net_state_next_buf == NULL)) {
+ net_state_next_buf = buffer;
+ } else {
+ if ((net_state_flags & NET_STATE_FLAG_RUN) && (net_state_flags & NET_STATE_FLAG_CTS)) {
+ do_release = net_xchg_next(buffer);
+ }
+ if (do_release) {
+ eos_bufq_push(&net_buf_q, buffer);
+ }
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+static int net_xchg(unsigned char *type, unsigned char *buffer, uint16_t *len, unsigned char flags) {
+ int rv = EOS_OK;
+ int _sync = 0;
+ unsigned char _type = *type;
+ uint16_t _len = *len;
+ uint8_t spi_dev = EOS_SPI_DEV_NET;
+
+ if (flags & EOS_NET_FLAG_ONEW) _type |= EOS_NET_MTYPE_FLAG_ONEW;
+ if (flags & EOS_NET_FLAG_REPL) _type |= EOS_NET_MTYPE_FLAG_REPL;
+ if (flags & EOS_NET_FLAG_SYNC) _sync = 1;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if ((flags & EOS_NET_FLAG_ONEW) && !(net_state_flags & NET_STATE_FLAG_RUN)) _sync = 1;
+
+ if (_sync && !(net_state_flags & NET_STATE_FLAG_RUN)) {
+ int _rv;
+
+ set_csr(mstatus, MSTATUS_MIE);
+ spi_dev = eos_spi_dev();
+ _rv = eos_spi_deselect();
+ if (_rv) return _rv;
+ clear_csr(mstatus, MSTATUS_MIE);
+ }
+
+ if (_sync) {
+ net_pause();
+ while (!(net_state_flags & NET_STATE_FLAG_CTS)) {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ clear_csr(mstatus, MSTATUS_MIE);
+ }
+ if (flags & EOS_NET_FLAG_SYNC) {
+ net_state_flags |= NET_STATE_FLAG_SYNC;
+ }
+ net_xchg_start(_type, buffer, _len);
+ if (flags & EOS_NET_FLAG_SYNC) {
+ if (flags & EOS_NET_FLAG_REPL) {
+ while (!(net_state_flags & NET_STATE_FLAG_CTS)) {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ clear_csr(mstatus, MSTATUS_MIE);
+ }
+ net_xchg_start(0, buffer, 0);
+ }
+ while (net_state_flags & NET_STATE_FLAG_XCHG) {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ clear_csr(mstatus, MSTATUS_MIE);
+ }
+ net_state_flags &= ~NET_STATE_FLAG_SYNC;
+ *type = net_state_type;
+ *len = net_state_len_rx;
+ }
+ net_resume();
+ } else {
+ if ((net_state_flags & NET_STATE_FLAG_RUN) && (net_state_flags & NET_STATE_FLAG_CTS)) {
+ net_xchg_start(_type, buffer, _len);
+ } else {
+ rv = eos_msgq_push(&net_send_q, _type, buffer, _len);
+ if (rv) eos_bufq_push(&net_buf_q, buffer);
+ }
+ }
+
+ set_csr(mstatus, MSTATUS_MIE);
+ if (spi_dev != EOS_SPI_DEV_NET) eos_spi_select(spi_dev);
+
+ return rv;
+}
+
+int eos_net_xchg(unsigned char *type, unsigned char *buffer, uint16_t *len) {
+ return net_xchg(type, buffer, len, (EOS_NET_FLAG_ONEW | EOS_NET_FLAG_SYNC | EOS_NET_FLAG_REPL));
+}
+
+int eos_net_send(unsigned char type, unsigned char *buffer, uint16_t len) {
+ return net_xchg(&type, buffer, &len, (EOS_NET_FLAG_ONEW | EOS_NET_FLAG_SYNC));
+}
+
+int eos_net_send_async(unsigned char type, unsigned char *buffer, uint16_t len, unsigned char more) {
+ return net_xchg(&type, buffer, &len, more ? EOS_NET_FLAG_ONEW : 0);
+}
+
+int _eos_net_send(unsigned char type, unsigned char *buffer, uint16_t len, unsigned char async, unsigned char more) {
+ if (async) {
+ eos_net_send_async(type, buffer, len, more);
+ } else {
+ eos_net_send(type, buffer, len);
+ }
+}
diff --git a/fw/fe310/eos/dev/net.h b/fw/fe310/eos/dev/net.h
new file mode 100644
index 0000000..2482a32
--- /dev/null
+++ b/fw/fe310/eos/dev/net.h
@@ -0,0 +1,47 @@
+#include <stdint.h>
+#include "../event.h"
+
+/* common */
+#define EOS_NET_MTU 1500
+#define EOS_NET_SIZE_BUF EOS_NET_MTU
+
+#define EOS_NET_MTYPE_SOCK 1
+#define EOS_NET_MTYPE_RNG 3
+#define EOS_NET_MTYPE_POWER 4
+
+#define EOS_NET_MTYPE_WIFI 5
+#define EOS_NET_MTYPE_CELL 6
+#define EOS_NET_MTYPE_SIP 7
+#define EOS_NET_MTYPE_APP 8
+
+#define EOS_NET_MAX_MTYPE 8
+
+#define EOS_NET_MTYPE_FLAG_ONEW 0x40
+#define EOS_NET_MTYPE_FLAG_REPL 0x80
+#define EOS_NET_MTYPE_FLAG_MASK 0xc0
+
+/* fe310 specific */
+#define EOS_NET_SIZE_BUFQ 2
+
+#define EOS_NET_FLAG_ONEW 0x1
+#define EOS_NET_FLAG_SYNC 0x2
+#define EOS_NET_FLAG_REPL 0x4
+
+int eos_net_init(uint8_t wakeup_cause);
+int eos_net_run(uint8_t wakeup_cause);
+void eos_net_start(void);
+void eos_net_stop(void);
+int eos_net_sleep(uint32_t timeout);
+
+void eos_net_bad_handler(unsigned char type, unsigned char *buffer, uint16_t len);
+void eos_net_set_handler(unsigned char type, eos_evt_handler_t handler);
+void eos_net_acquire_for_evt(unsigned char type, char acq);
+
+void eos_net_acquire(void);
+void eos_net_release(void);
+unsigned char *eos_net_alloc(void);
+void eos_net_free(unsigned char *buffer, unsigned char more);
+int eos_net_xchg(unsigned char *type, unsigned char *buffer, uint16_t *len);
+int eos_net_send(unsigned char type, unsigned char *buffer, uint16_t len);
+int eos_net_send_async(unsigned char type, unsigned char *buffer, uint16_t len, unsigned char more);
+int _eos_net_send(unsigned char type, unsigned char *buffer, uint16_t len, unsigned char async, unsigned char more);
diff --git a/fw/fe310/eos/dev/ov2640.c b/fw/fe310/eos/dev/ov2640.c
new file mode 100644
index 0000000..e84a59b
--- /dev/null
+++ b/fw/fe310/eos/dev/ov2640.c
@@ -0,0 +1,861 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "eos.h"
+#include "soc/timer.h"
+#include "soc/i2c.h"
+
+#include "cam.h"
+#include "ov2640_regs.h"
+#include "ov2640.h"
+
+#define XCLK_FREQ 24000000
+//#define XCLK_FREQ 12000000
+
+#define CIF_WIDTH (400)
+#define CIF_HEIGHT (296)
+
+#define SVGA_WIDTH (800)
+#define SVGA_HEIGHT (600)
+
+#define UXGA_WIDTH (1600)
+#define UXGA_HEIGHT (1200)
+
+static const uint8_t default_regs[][2] = {
+
+// From Linux Driver.
+
+ {BANK_SEL, BANK_SEL_DSP},
+ {0x2c, 0xff},
+ {0x2e, 0xdf},
+ {BANK_SEL, BANK_SEL_SENSOR},
+ {0x3c, 0x32},
+// {CLKRC, CLKRC_DOUBLE | 0x02},
+ {CLKRC, 0x01},
+ {COM2, COM2_OUT_DRIVE_3x},
+ {REG04, REG04_SET(REG04_HFLIP_IMG | REG04_VFLIP_IMG | REG04_VREF_EN | REG04_HREF_EN)},
+ {COM8, COM8_SET(COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN)},
+ {COM9, COM9_AGC_SET(COM9_AGC_GAIN_8x)},
+ {0x2c, 0x0c},
+ {0x33, 0x78},
+ {0x3a, 0x33},
+ {0x3b, 0xfb},
+ {0x3e, 0x00},
+ {0x43, 0x11},
+ {0x16, 0x10},
+ {0x39, 0x02},
+ {0x35, 0x88},
+ {0x22, 0x0a},
+ {0x37, 0x40},
+ {0x23, 0x00},
+ {ARCOM2, 0xa0},
+ {0x06, 0x02},
+ {0x06, 0x88},
+ {0x07, 0xc0},
+ {0x0d, 0xb7},
+ {0x0e, 0x01},
+ {0x4c, 0x00},
+ {0x4a, 0x81},
+ {0x21, 0x99},
+ {AEW, 0x40},
+ {AEB, 0x38},
+ {VV, VV_AGC_TH_SET(0x08, 0x02)},
+ {0x5c, 0x00},
+ {0x63, 0x00},
+ {FLL, 0x22},
+ {COM3, COM3_BAND_SET(COM3_BAND_AUTO)},
+ {REG5D, 0x55},
+ {REG5E, 0x7d},
+ {REG5F, 0x7d},
+ {REG60, 0x55},
+ {HISTO_LOW, 0x70},
+ {HISTO_HIGH, 0x80},
+ {0x7c, 0x05},
+ {0x20, 0x80},
+ {0x28, 0x30},
+ {0x6c, 0x00},
+ {0x6d, 0x80},
+ {0x6e, 0x00},
+ {0x70, 0x02},
+ {0x71, 0x94},
+ {0x73, 0xc1},
+ {0x3d, 0x34},
+ {COM7, COM7_RES_UXGA | COM7_ZOOM_EN},
+ {0x5a, 0x57},
+ {COM25, 0x00},
+ {BD50, 0xbb},
+ {BD60, 0x9c},
+ {BANK_SEL, BANK_SEL_DSP},
+ {0xe5, 0x7f},
+ {MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL},
+ {0x41, 0x24},
+ {RESET, RESET_JPEG | RESET_DVP},
+ {0x76, 0xff},
+ {0x33, 0xa0},
+ {0x42, 0x20},
+ {0x43, 0x18},
+ {0x4c, 0x00},
+ {CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10},
+ {0x88, 0x3f},
+ {0xd7, 0x03},
+ {0xd9, 0x10},
+ {R_DVP_SP, R_DVP_SP_AUTO_MODE | 0x2},
+ {0xc8, 0x08},
+ {0xc9, 0x80},
+ {BPADDR, 0x00},
+ {BPDATA, 0x00},
+ {BPADDR, 0x03},
+ {BPDATA, 0x48},
+ {BPDATA, 0x48},
+ {BPADDR, 0x08},
+ {BPDATA, 0x20},
+ {BPDATA, 0x10},
+ {BPDATA, 0x0e},
+ {0x90, 0x00},
+ {0x91, 0x0e},
+ {0x91, 0x1a},
+ {0x91, 0x31},
+ {0x91, 0x5a},
+ {0x91, 0x69},
+ {0x91, 0x75},
+ {0x91, 0x7e},
+ {0x91, 0x88},
+ {0x91, 0x8f},
+ {0x91, 0x96},
+ {0x91, 0xa3},
+ {0x91, 0xaf},
+ {0x91, 0xc4},
+ {0x91, 0xd7},
+ {0x91, 0xe8},
+ {0x91, 0x20},
+ {0x92, 0x00},
+ {0x93, 0x06},
+ {0x93, 0xe3},
+ {0x93, 0x03},
+ {0x93, 0x03},
+ {0x93, 0x00},
+ {0x93, 0x02},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x96, 0x00},
+ {0x97, 0x08},
+ {0x97, 0x19},
+ {0x97, 0x02},
+ {0x97, 0x0c},
+ {0x97, 0x24},
+ {0x97, 0x30},
+ {0x97, 0x28},
+ {0x97, 0x26},
+ {0x97, 0x02},
+ {0x97, 0x98},
+ {0x97, 0x80},
+ {0x97, 0x00},
+ {0x97, 0x00},
+ {0xa4, 0x00},
+ {0xa8, 0x00},
+ {0xc5, 0x11},
+ {0xc6, 0x51},
+ {0xbf, 0x80},
+ {0xc7, 0x10}, /* simple AWB */
+ {0xb6, 0x66},
+ {0xb8, 0xA5},
+ {0xb7, 0x64},
+ {0xb9, 0x7C},
+ {0xb3, 0xaf},
+ {0xb4, 0x97},
+ {0xb5, 0xFF},
+ {0xb0, 0xC5},
+ {0xb1, 0x94},
+ {0xb2, 0x0f},
+ {0xc4, 0x5c},
+ {0xa6, 0x00},
+ {0xa7, 0x20},
+ {0xa7, 0xd8},
+ {0xa7, 0x1b},
+ {0xa7, 0x31},
+ {0xa7, 0x00},
+ {0xa7, 0x18},
+ {0xa7, 0x20},
+ {0xa7, 0xd8},
+ {0xa7, 0x19},
+ {0xa7, 0x31},
+ {0xa7, 0x00},
+ {0xa7, 0x18},
+ {0xa7, 0x20},
+ {0xa7, 0xd8},
+ {0xa7, 0x19},
+ {0xa7, 0x31},
+ {0xa7, 0x00},
+ {0xa7, 0x18},
+ {0x7f, 0x00},
+ {0xe5, 0x1f},
+ {0xe1, 0x77},
+ {0xdd, 0x7f},
+ {CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN | CTRL0_RGB_EN},
+
+// OpenMV Custom.
+
+ {BANK_SEL, BANK_SEL_SENSOR},
+ {0x0f, 0x4b},
+ {COM1, 0x8f},
+
+// End.
+
+ {0xff, 0xff},
+};
+
+// Looks really bad.
+//static const uint8_t cif_regs[][2] = {
+// {BANK_SEL, BANK_SEL_SENSOR},
+// {COM7, COM7_RES_CIF},
+// {COM1, 0x06 | 0x80},
+// {HSTART, 0x11},
+// {HSTOP, 0x43},
+// {VSTART, 0x01}, // 0x01 fixes issue with garbage pixels in the image...
+// {VSTOP, 0x97},
+// {REG32, 0x09},
+// {BANK_SEL, BANK_SEL_DSP},
+// {RESET, RESET_DVP},
+// {SIZEL, SIZEL_HSIZE8_11_SET(CIF_WIDTH) | SIZEL_HSIZE8_SET(CIF_WIDTH) | SIZEL_VSIZE8_SET(CIF_HEIGHT)},
+// {HSIZE8, HSIZE8_SET(CIF_WIDTH)},
+// {VSIZE8, VSIZE8_SET(CIF_HEIGHT)},
+// {CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN},
+// {0, 0},
+//};
+
+static const uint8_t svga_regs[][2] = {
+ {BANK_SEL, BANK_SEL_SENSOR},
+ {COM7, COM7_RES_SVGA},
+ {COM1, 0x0A | 0x80},
+ {HSTART, 0x11},
+ {HSTOP, 0x43},
+ {VSTART, 0x01}, // 0x01 fixes issue with garbage pixels in the image...
+ {VSTOP, 0x97},
+ {REG32, 0x09},
+ {BANK_SEL, BANK_SEL_DSP},
+ {RESET, RESET_DVP},
+ {SIZEL, SIZEL_HSIZE8_11_SET(SVGA_WIDTH) | SIZEL_HSIZE8_SET(SVGA_WIDTH) | SIZEL_VSIZE8_SET(SVGA_HEIGHT)},
+ {HSIZE8, HSIZE8_SET(SVGA_WIDTH)},
+ {VSIZE8, VSIZE8_SET(SVGA_HEIGHT)},
+ {CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t uxga_regs[][2] = {
+ {BANK_SEL, BANK_SEL_SENSOR},
+ {COM7, COM7_RES_UXGA},
+ {COM1, 0x0F | 0x80},
+ {HSTART, 0x11},
+ {HSTOP, 0x75},
+ {VSTART, 0x01},
+ {VSTOP, 0x97},
+ {REG32, 0x36},
+ {BANK_SEL, BANK_SEL_DSP},
+ {RESET, RESET_DVP},
+ {SIZEL, SIZEL_HSIZE8_11_SET(UXGA_WIDTH) | SIZEL_HSIZE8_SET(UXGA_WIDTH) | SIZEL_VSIZE8_SET(UXGA_HEIGHT)},
+ {HSIZE8, HSIZE8_SET(UXGA_WIDTH)},
+ {VSIZE8, VSIZE8_SET(UXGA_HEIGHT)},
+ {CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t yuv422_regs[][2] = {
+ {BANK_SEL, BANK_SEL_DSP},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {IMAGE_MODE, IMAGE_MODE_YUV422},
+ {0xd7, 0x03},
+ {0x33, 0xa0},
+ {0xe5, 0x1f},
+ {0xe1, 0x67},
+ {RESET, 0x00},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t rgb565_regs[][2] = {
+ {BANK_SEL, BANK_SEL_DSP},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {IMAGE_MODE, IMAGE_MODE_RGB565},
+ {0xd7, 0x03},
+ {RESET, 0x00},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t bayer_regs[][2] = {
+ {BANK_SEL, BANK_SEL_DSP},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {IMAGE_MODE, IMAGE_MODE_RAW10},
+ {0xd7, 0x03},
+ {RESET, 0x00},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t jpeg_regs[][2] = {
+ {BANK_SEL, BANK_SEL_DSP},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {IMAGE_MODE, IMAGE_MODE_JPEG_EN},
+ {0xd7, 0x03},
+ {RESET, 0x00},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {0xff, 0xff},
+};
+
+#define NUM_BRIGHTNESS_LEVELS (5)
+static const uint8_t brightness_regs[NUM_BRIGHTNESS_LEVELS + 1][5] = {
+ {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA},
+ {0x00, 0x04, 0x09, 0x00, 0x00}, /* -2 */
+ {0x00, 0x04, 0x09, 0x10, 0x00}, /* -1 */
+ {0x00, 0x04, 0x09, 0x20, 0x00}, /* 0 */
+ {0x00, 0x04, 0x09, 0x30, 0x00}, /* +1 */
+ {0x00, 0x04, 0x09, 0x40, 0x00}, /* +2 */
+};
+
+#define NUM_CONTRAST_LEVELS (5)
+static const uint8_t contrast_regs[NUM_CONTRAST_LEVELS + 1][7] = {
+ {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA, BPDATA, BPDATA},
+ {0x00, 0x04, 0x07, 0x20, 0x18, 0x34, 0x06}, /* -2 */
+ {0x00, 0x04, 0x07, 0x20, 0x1c, 0x2a, 0x06}, /* -1 */
+ {0x00, 0x04, 0x07, 0x20, 0x20, 0x20, 0x06}, /* 0 */
+ {0x00, 0x04, 0x07, 0x20, 0x24, 0x16, 0x06}, /* +1 */
+ {0x00, 0x04, 0x07, 0x20, 0x28, 0x0c, 0x06}, /* +2 */
+};
+
+#define NUM_SATURATION_LEVELS (5)
+static const uint8_t saturation_regs[NUM_SATURATION_LEVELS + 1][5] = {
+ {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA},
+ {0x00, 0x02, 0x03, 0x28, 0x28}, /* -2 */
+ {0x00, 0x02, 0x03, 0x38, 0x38}, /* -1 */
+ {0x00, 0x02, 0x03, 0x48, 0x48}, /* 0 */
+ {0x00, 0x02, 0x03, 0x58, 0x58}, /* +1 */
+ {0x00, 0x02, 0x03, 0x68, 0x68}, /* +2 */
+};
+
+static int reg_read(int8_t reg, uint8_t *data) {
+ return eos_i2c_read8(OV2640_ADDR, reg, data, 1);
+}
+
+static int reg_write(uint8_t reg, uint8_t data) {
+ return eos_i2c_write8(OV2640_ADDR, reg, &data, 1);
+}
+
+static int regarr_write(const uint8_t (*regs)[2]) {
+ int i, rv;
+
+ i = 0;
+ rv = EOS_OK;
+
+ while ((regs[i][0] != 0xff) || (regs[i][1] != 0xff)) {
+ if (!rv) rv = reg_write(regs[i][0], regs[i][1]);
+ i++;
+ }
+
+ return rv;
+}
+
+int eos_ov2640_init(void) {
+ int rv;
+
+ // Reset all registers
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_write(COM7, COM7_SRST);
+ if (rv) return rv;
+
+ // Delay 5 ms
+ eos_time_sleep(5);
+
+ // Write default regsiters
+ rv = regarr_write(default_regs);
+ if (rv) return rv;
+
+ // Delay 300 ms
+ eos_time_sleep(300);
+
+ return EOS_OK;
+}
+
+int eos_ov2640_sleep(int enable) {
+ uint8_t reg;
+ int rv;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM2, &reg);
+ if (rv) return rv;
+
+ if (enable) {
+ reg |= COM2_STDBY;
+ } else {
+ reg &= ~COM2_STDBY;
+ }
+
+ // Write back register
+ return reg_write(COM2, reg);
+}
+
+int eos_ov2640_set_pixfmt(pixformat_t fmt) {
+ const uint8_t (*regs)[2];
+
+ switch (fmt) {
+ case PIXFORMAT_RGB565:
+ regs = rgb565_regs;
+ break;
+ case PIXFORMAT_YUV422:
+ case PIXFORMAT_GRAYSCALE:
+ regs = yuv422_regs;
+ break;
+ case PIXFORMAT_BAYER:
+ regs = bayer_regs;
+ break;
+ case PIXFORMAT_JPEG:
+ regs = jpeg_regs;
+ break;
+ default:
+ return EOS_ERR;
+ }
+
+ return regarr_write(regs);
+}
+
+int eos_ov2640_set_framesize(framesize_t framesize) {
+ const uint8_t (*regs)[2];
+ uint16_t sensor_w = 0;
+ uint16_t sensor_h = 0;
+ uint16_t w = _eos_cam_resolution[framesize][0];
+ uint16_t h = _eos_cam_resolution[framesize][1];
+ int rv;
+
+ if ((w % 4) || (h % 4) || (w > UXGA_WIDTH) || (h > UXGA_HEIGHT)) { // w/h must be divisble by 4
+ return EOS_ERR;
+ }
+
+ // Looks really bad.
+ /* if ((w <= CIF_WIDTH) && (h <= CIF_HEIGHT)) {
+ regs = cif_regs;
+ sensor_w = CIF_WIDTH;
+ sensor_h = CIF_HEIGHT;
+ } else */ if ((w <= SVGA_WIDTH) && (h <= SVGA_HEIGHT)) {
+ regs = svga_regs;
+ sensor_w = SVGA_WIDTH;
+ sensor_h = SVGA_HEIGHT;
+ } else {
+ regs = uxga_regs;
+ sensor_w = UXGA_WIDTH;
+ sensor_h = UXGA_HEIGHT;
+ }
+
+ // Write setup regsiters
+ rv = regarr_write(regs);
+ if (rv) return rv;
+
+ uint64_t tmp_div = IM_MIN(sensor_w / w, sensor_h / h);
+ uint16_t log_div = IM_MIN(IM_LOG2(tmp_div) - 1, 3);
+ uint16_t div = 1 << log_div;
+ uint16_t w_mul = w * div;
+ uint16_t h_mul = h * div;
+ uint16_t x_off = (sensor_w - w_mul) / 2;
+ uint16_t y_off = (sensor_h - h_mul) / 2;
+
+ rv = EOS_OK;
+ if (!rv) rv = reg_write(CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(log_div) | CTRLI_H_DIV_SET(log_div));
+ if (!rv) rv = reg_write(HSIZE, HSIZE_SET(w_mul));
+ if (!rv) rv = reg_write(VSIZE, VSIZE_SET(h_mul));
+ if (!rv) rv = reg_write(XOFFL, XOFFL_SET(x_off));
+ if (!rv) rv = reg_write(YOFFL, YOFFL_SET(y_off));
+ if (!rv) rv = reg_write(VHYX, VHYX_HSIZE_SET(w_mul) | VHYX_VSIZE_SET(h_mul) | VHYX_XOFF_SET(x_off) | VHYX_YOFF_SET(y_off));
+ if (!rv) rv = reg_write(TEST, TEST_HSIZE_SET(w_mul));
+ if (!rv) rv = reg_write(ZMOW, ZMOW_OUTW_SET(w));
+ if (!rv) rv = reg_write(ZMOH, ZMOH_OUTH_SET(h));
+ if (!rv) rv = reg_write(ZMHH, ZMHH_OUTW_SET(w) | ZMHH_OUTH_SET(h));
+ if (!rv) rv = reg_write(R_DVP_SP, div);
+ if (!rv) rv = reg_write(RESET, 0x00);
+
+ return rv;
+}
+
+int eos_ov2640_set_contrast(int level) {
+ int rv = EOS_OK;
+
+ level += (NUM_CONTRAST_LEVELS / 2) + 1;
+ if (level <= 0 || level > NUM_CONTRAST_LEVELS) {
+ return EOS_ERR;
+ }
+
+ /* Switch to DSP register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+
+ /* Write contrast registers */
+ for (int i=0; i<sizeof(contrast_regs[0])/sizeof(contrast_regs[0][0]); i++) {
+ if (!rv) rv = reg_write(contrast_regs[0][i], contrast_regs[level][i]);
+ }
+
+ return rv;
+}
+
+int eos_ov2640_set_brightness(int level) {
+ int rv = EOS_OK;
+
+ level += (NUM_BRIGHTNESS_LEVELS / 2) + 1;
+ if (level <= 0 || level > NUM_BRIGHTNESS_LEVELS) {
+ return EOS_ERR;
+ }
+
+ /* Switch to DSP register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+
+ /* Write brightness registers */
+ for (int i=0; i<sizeof(brightness_regs[0])/sizeof(brightness_regs[0][0]); i++) {
+ if (!rv) rv = reg_write(brightness_regs[0][i], brightness_regs[level][i]);
+ }
+
+ return rv;
+}
+
+int eos_ov2640_set_saturation(int level) {
+ int rv = EOS_OK;
+
+ level += (NUM_SATURATION_LEVELS / 2) + 1;
+ if (level <= 0 || level > NUM_SATURATION_LEVELS) {
+ return EOS_ERR;
+ }
+
+ /* Switch to DSP register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+
+ /* Write saturation registers */
+ for (int i=0; i<sizeof(saturation_regs[0])/sizeof(saturation_regs[0][0]); i++) {
+ if (!rv) rv = reg_write(saturation_regs[0][i], saturation_regs[level][i]);
+ }
+
+ return rv;
+}
+
+int eos_ov2640_set_gainceiling(gainceiling_t gainceiling) {
+ int rv = EOS_OK;
+
+ /* Switch to SENSOR register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+
+ /* Write gain ceiling register */
+ if (!rv) rv = reg_write(COM9, COM9_AGC_SET(gainceiling));
+
+ return rv;
+}
+
+int eos_ov2640_set_quality(int qs) {
+ int rv = EOS_OK;
+
+ /* Switch to DSP register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+
+ /* Write QS register */
+ if (!rv) rv = reg_write(QS, qs);
+
+ return rv;
+}
+
+int eos_ov2640_set_colorbar(int enable) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM7, &reg);
+ if (rv) return rv;
+
+ if (enable) {
+ reg |= COM7_COLOR_BAR;
+ } else {
+ reg &= ~COM7_COLOR_BAR;
+ }
+
+ return reg_write(COM7, reg);
+}
+
+int eos_ov2640_set_auto_gain(int enable, float gain_db, float gain_db_ceiling) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM8, &reg);
+ if (rv) return rv;
+
+ rv = reg_write(COM8, (reg & (~COM8_AGC_EN)) | (enable ? COM8_AGC_EN : 0));
+ if (rv) return rv;
+
+ rv = EOS_OK;
+ if (enable && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) {
+ float gain_ceiling = IM_MAX(IM_MIN(expf((gain_db_ceiling / 20.0) * logf(10.0)), 128.0), 2.0);
+
+ if (!rv) rv = reg_read(COM9, &reg);
+ if (!rv) rv = reg_write(COM9, (reg & 0x1F) | (((int)ceilf(log2f(gain_ceiling)) - 1) << 5));
+ }
+
+ if (!enable && (!isnanf(gain_db)) && (!isinff(gain_db))) {
+ float gain = IM_MAX(IM_MIN(expf((gain_db / 20.0) * logf(10.0)), 32.0), 1.0);
+
+ int gain_temp = roundf(log2f(IM_MAX(gain / 2.0, 1.0)));
+ int gain_hi = 0xF >> (4 - gain_temp);
+ int gain_lo = IM_MIN(roundf(((gain / (1 << gain_temp)) - 1.0) * 16.0), 15);
+
+ if (!rv) rv = reg_write(GAIN, (gain_hi << 4) | (gain_lo << 0));
+ }
+
+ return rv;
+}
+
+int eos_ov2640_get_gain_db(float *gain_db) {
+ int rv = EOS_OK;
+ uint8_t reg, gain;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM8, &reg);
+ if (rv) return rv;
+
+ // DISABLED
+ // if (reg & COM8_AGC_EN) {
+ // rv = reg_write(COM8, reg & (~COM8_AGC_EN));
+ // if (rv) return rv;
+ // }
+ // DISABLED
+
+ rv = reg_read(GAIN, &gain);
+ if (rv) return rv;
+
+ // DISABLED
+ // if (reg & COM8_AGC_EN) {
+ // rv = reg_write(COM8, reg | COM8_AGC_EN);
+ // if (rv) return rv;
+ // }
+ // DISABLED
+
+ int hi_gain = 1 << (((gain >> 7) & 1) + ((gain >> 6) & 1) + ((gain >> 5) & 1) + ((gain >> 4) & 1));
+ float lo_gain = 1.0 + (((gain >> 0) & 0xF) / 16.0);
+ *gain_db = 20.0 * (logf(hi_gain * lo_gain) / logf(10.0));
+
+ return EOS_OK;
+}
+
+int eos_ov2640_set_auto_exposure(int enable, int exposure_us) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM8, &reg);
+ if (rv) return rv;
+
+ rv = reg_write(COM8, COM8_SET_AEC(reg, (enable != 0)));
+ if (rv) return rv;
+
+ if (!enable && (exposure_us >= 0)) {
+ rv = reg_read(COM7, &reg);
+ if (rv) return rv;
+
+ int t_line = 0;
+
+ if (COM7_GET_RES(reg) == COM7_RES_UXGA) t_line = 1600 + 322;
+ if (COM7_GET_RES(reg) == COM7_RES_SVGA) t_line = 800 + 390;
+ if (COM7_GET_RES(reg) == COM7_RES_CIF) t_line = 400 + 195;
+
+ rv = reg_read(CLKRC, &reg);
+ if (rv) return rv;
+
+ int pll_mult = ((reg & CLKRC_DOUBLE) ? 2 : 1) * 3;
+ int clk_rc = (reg & CLKRC_DIVIDER_MASK) + 2;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (rv) return rv;
+
+ rv = reg_read(IMAGE_MODE, &reg);
+ if (rv) return rv;
+
+ int t_pclk = 0;
+
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_YUV422) t_pclk = 2;
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RAW10) t_pclk = 1;
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RGB565) t_pclk = 2;
+
+ int exposure = IM_MAX(IM_MIN(((exposure_us*(((XCLK_FREQ/clk_rc)*pll_mult)/1000000))/t_pclk)/t_line,0xFFFF),0x0000);
+
+ if (!rv) rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+
+ if (!rv) rv = reg_read(REG04, &reg);
+ if (!rv) rv = reg_write(REG04, (reg & 0xFC) | ((exposure >> 0) & 0x3));
+
+ if (!rv) rv = reg_read(AEC, &reg);
+ if (!rv) rv = reg_write(AEC, (reg & 0x00) | ((exposure >> 2) & 0xFF));
+
+ if (!rv) rv = reg_read(REG45, &reg);
+ if (!rv) rv = reg_write(REG45, (reg & 0xC0) | ((exposure >> 10) & 0x3F));
+ }
+
+ return rv;
+}
+
+int eos_ov2640_get_exposure_us(int *exposure_us) {
+ int rv = EOS_OK;
+ uint8_t reg, aec_10, aec_92, aec_1510;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM8, &reg);
+ if (rv) return rv;
+
+ // DISABLED
+ // if (reg & COM8_AEC_EN) {
+ // rv = reg_write(COM8, reg & (~COM8_AEC_EN));
+ // if (rv) return rv;
+ // }
+ // DISABLED
+
+ rv = reg_read(REG04, &aec_10);
+ if (rv) return rv;
+
+ rv = reg_read(AEC, &aec_92);
+ if (rv) return rv;
+
+ rv = reg_read(REG45, &aec_1510);
+ if (rv) return rv;
+
+ // DISABLED
+ // if (reg & COM8_AEC_EN) {
+ // rv = reg_write(COM8, reg | COM8_AEC_EN);
+ // if (rv) return rv;
+ // }
+ // DISABLED
+
+ rv = reg_read(COM7, &reg);
+ if (rv) return rv;
+
+ int t_line = 0;
+
+ if (COM7_GET_RES(reg) == COM7_RES_UXGA) t_line = 1600 + 322;
+ if (COM7_GET_RES(reg) == COM7_RES_SVGA) t_line = 800 + 390;
+ if (COM7_GET_RES(reg) == COM7_RES_CIF) t_line = 400 + 195;
+
+ rv = reg_read(CLKRC, &reg);
+ if (rv) return rv;
+
+ int pll_mult = ((reg & CLKRC_DOUBLE) ? 2 : 1) * 3;
+ int clk_rc = (reg & CLKRC_DIVIDER_MASK) + 2;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (rv) return rv;
+
+ rv = reg_read(IMAGE_MODE, &reg);
+ if (rv) return rv;
+
+ int t_pclk = 0;
+
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_YUV422) t_pclk = 2;
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RAW10) t_pclk = 1;
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RGB565) t_pclk = 2;
+
+ uint16_t exposure = ((aec_1510 & 0x3F) << 10) + ((aec_92 & 0xFF) << 2) + ((aec_10 & 0x3) << 0);
+ *exposure_us = (exposure*t_line*t_pclk)/(((XCLK_FREQ/clk_rc)*pll_mult)/1000000);
+
+ return EOS_OK;
+}
+
+int eos_ov2640_set_auto_whitebal(int enable, float r_gain_db, float g_gain_db, float b_gain_db) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (rv) return rv;
+
+ rv = reg_read(CTRL1, &reg);
+ if (rv) return rv;
+
+ rv = reg_write(CTRL1, (reg & (~CTRL1_AWB)) | (enable ? CTRL1_AWB : 0));
+ if (rv) return rv;
+
+ if (!enable && (!isnanf(r_gain_db)) && (!isnanf(g_gain_db)) && (!isnanf(b_gain_db))
+ && (!isinff(r_gain_db)) && (!isinff(g_gain_db)) && (!isinff(b_gain_db))) {
+ }
+
+ return rv;
+}
+
+int eos_ov2640_set_hmirror(int enable) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(REG04, &reg);
+ if (rv) return rv;
+
+ if (!enable) { // Already mirrored.
+ reg |= REG04_HFLIP_IMG;
+ } else {
+ reg &= ~REG04_HFLIP_IMG;
+ }
+
+ return reg_write(REG04, reg);
+}
+
+int eos_ov2640_set_vflip(int enable) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(REG04, &reg);
+ if (rv) return rv;
+
+ if (!enable) { // Already flipped.
+ reg |= REG04_VFLIP_IMG | REG04_VREF_EN;
+ } else {
+ reg &= ~(REG04_VFLIP_IMG | REG04_VREF_EN);
+ }
+
+ return reg_write(REG04, reg);
+}
+
+int eos_ov2640_set_effect(sde_t sde) {
+ int rv = EOS_OK;
+
+ switch (sde) {
+ case SDE_NEGATIVE:
+ if (!rv) rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (!rv) rv = reg_write(BPADDR, 0x00);
+ if (!rv) rv = reg_write(BPDATA, 0x40);
+ if (!rv) rv = reg_write(BPADDR, 0x05);
+ if (!rv) rv = reg_write(BPDATA, 0x80);
+ if (!rv) rv = reg_write(BPDATA, 0x80);
+ break;
+ case SDE_NORMAL:
+ if (!rv) rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (!rv) rv = reg_write(BPADDR, 0x00);
+ if (!rv) rv = reg_write(BPDATA, 0x00);
+ if (!rv) rv = reg_write(BPADDR, 0x05);
+ if (!rv) rv = reg_write(BPDATA, 0x80);
+ if (!rv) rv = reg_write(BPDATA, 0x80);
+ break;
+ default:
+ return EOS_ERR;
+ }
+
+ return rv;
+}
diff --git a/fw/fe310/eos/dev/ov2640.h b/fw/fe310/eos/dev/ov2640.h
new file mode 100644
index 0000000..3d08c2a
--- /dev/null
+++ b/fw/fe310/eos/dev/ov2640.h
@@ -0,0 +1,8 @@
+#include <stdint.h>
+
+#define OV2640_ADDR 0x30
+
+int eos_ov2640_init(void);
+int eos_ov2640_sleep(int enable);
+int eos_ov2640_set_pixfmt(pixformat_t fmt);
+int eos_ov2640_set_framesize(framesize_t framesize);
diff --git a/fw/fe310/eos/dev/ov2640_regs.h b/fw/fe310/eos/dev/ov2640_regs.h
new file mode 100644
index 0000000..deb7521
--- /dev/null
+++ b/fw/fe310/eos/dev/ov2640_regs.h
@@ -0,0 +1,245 @@
+/*
+ * This file is part of the OpenMV project.
+ *
+ * Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
+ * Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
+ *
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV2640 register definitions.
+ */
+#ifndef __REG_REGS_H__
+#define __REG_REGS_H__
+
+/* DSP register bank FF=0x00*/
+
+#define QS 0x44
+#define HSIZE 0x51
+#define VSIZE 0x52
+#define XOFFL 0x53
+#define YOFFL 0x54
+#define VHYX 0x55
+#define DPRP 0x56
+#define TEST 0x57
+#define ZMOW 0x5A
+#define ZMOH 0x5B
+#define ZMHH 0x5C
+#define BPADDR 0x7C
+#define BPDATA 0x7D
+#define SIZEL 0x8C
+#define HSIZE8 0xC0
+#define VSIZE8 0xC1
+#define CTRL1 0xC3
+#define MS_SP 0xF0
+#define SS_ID 0xF7
+#define SS_CTRL 0xF7
+#define MC_AL 0xFA
+#define MC_AH 0xFB
+#define MC_D 0xFC
+#define P_CMD 0xFD
+#define P_STATUS 0xFE
+
+#define CTRLI 0x50
+#define CTRLI_LP_DP 0x80
+#define CTRLI_ROUND 0x40
+
+#define CTRL0 0xC2
+#define CTRL0_AEC_EN 0x80
+#define CTRL0_AEC_SEL 0x40
+#define CTRL0_STAT_SEL 0x20
+#define CTRL0_VFIRST 0x10
+#define CTRL0_YUV422 0x08
+#define CTRL0_YUV_EN 0x04
+#define CTRL0_RGB_EN 0x02
+#define CTRL0_RAW_EN 0x01
+
+#define CTRL2 0x86
+#define CTRL2_DCW_EN 0x20
+#define CTRL2_SDE_EN 0x10
+#define CTRL2_UV_ADJ_EN 0x08
+#define CTRL2_UV_AVG_EN 0x04
+#define CTRL2_CMX_EN 0x01
+
+#define CTRL3 0x87
+#define CTRL3_BPC_EN 0x80
+#define CTRL3_WPC_EN 0x40
+#define R_DVP_SP 0xD3
+#define R_DVP_SP_AUTO_MODE 0x80
+
+#define R_BYPASS 0x05
+#define R_BYPASS_DSP_EN 0x00
+#define R_BYPASS_DSP_BYPAS 0x01
+
+#define IMAGE_MODE 0xDA
+#define IMAGE_MODE_Y8_DVP_EN 0x40
+#define IMAGE_MODE_JPEG_EN 0x10
+#define IMAGE_MODE_YUV422 0x00
+#define IMAGE_MODE_RAW10 0x04
+#define IMAGE_MODE_RGB565 0x09
+#define IMAGE_MODE_HREF_VSYNC 0x02
+#define IMAGE_MODE_LBYTE_FIRST 0x01
+#define IMAGE_MODE_GET_FMT(x) ((x)&0xC)
+
+#define RESET 0xE0
+#define RESET_MICROC 0x40
+#define RESET_SCCB 0x20
+#define RESET_JPEG 0x10
+#define RESET_DVP 0x04
+#define RESET_IPU 0x02
+#define RESET_CIF 0x01
+
+#define MC_BIST 0xF9
+#define MC_BIST_RESET 0x80
+#define MC_BIST_BOOT_ROM_SEL 0x40
+#define MC_BIST_12KB_SEL 0x20
+#define MC_BIST_12KB_MASK 0x30
+#define MC_BIST_512KB_SEL 0x08
+#define MC_BIST_512KB_MASK 0x0C
+#define MC_BIST_BUSY_BIT_R 0x02
+#define MC_BIST_MC_RES_ONE_SH_W 0x02
+#define MC_BIST_LAUNCH 0x01
+
+#define BANK_SEL 0xFF
+#define BANK_SEL_DSP 0x00
+#define BANK_SEL_SENSOR 0x01
+
+/* Sensor register bank FF=0x01*/
+
+#define GAIN 0x00
+#define COM1 0x03
+#define REG_PID 0x0A
+#define REG_VER 0x0B
+#define COM4 0x0D
+#define AEC 0x10
+
+#define CLKRC 0x11
+#define CLKRC_DOUBLE 0x80
+#define CLKRC_DIVIDER_MASK 0x3F
+
+#define COM10 0x15
+#define HSTART 0x17
+#define HSTOP 0x18
+#define VSTART 0x19
+#define VSTOP 0x1A
+#define MIDH 0x1C
+#define MIDL 0x1D
+#define AEW 0x24
+#define AEB 0x25
+#define REG2A 0x2A
+#define FRARL 0x2B
+#define ADDVSL 0x2D
+#define ADDVSH 0x2E
+#define YAVG 0x2F
+#define HSDY 0x30
+#define HEDY 0x31
+#define ARCOM2 0x34
+#define REG45 0x45
+#define FLL 0x46
+#define FLH 0x47
+#define COM19 0x48
+#define ZOOMS 0x49
+#define COM22 0x4B
+#define COM25 0x4E
+#define BD50 0x4F
+#define BD60 0x50
+#define REG5D 0x5D
+#define REG5E 0x5E
+#define REG5F 0x5F
+#define REG60 0x60
+#define HISTO_LOW 0x61
+#define HISTO_HIGH 0x62
+
+#define REG04 0x04
+#define REG04_DEFAULT 0x28
+#define REG04_HFLIP_IMG 0x80
+#define REG04_VFLIP_IMG 0x40
+#define REG04_VREF_EN 0x10
+#define REG04_HREF_EN 0x08
+#define REG04_SET(x) (REG04_DEFAULT|x)
+
+#define REG08 0x08
+#define COM2 0x09
+#define COM2_STDBY 0x10
+#define COM2_OUT_DRIVE_1x 0x00
+#define COM2_OUT_DRIVE_2x 0x01
+#define COM2_OUT_DRIVE_3x 0x02
+#define COM2_OUT_DRIVE_4x 0x03
+
+#define COM3 0x0C
+#define COM3_DEFAULT 0x38
+#define COM3_BAND_50Hz 0x04
+#define COM3_BAND_60Hz 0x00
+#define COM3_BAND_AUTO 0x02
+#define COM3_BAND_SET(x) (COM3_DEFAULT|x)
+
+#define COM7 0x12
+#define COM7_SRST 0x80
+#define COM7_RES_UXGA 0x00 /* UXGA */
+#define COM7_RES_SVGA 0x40 /* SVGA */
+#define COM7_RES_CIF 0x20 /* CIF */
+#define COM7_ZOOM_EN 0x04 /* Enable Zoom */
+#define COM7_COLOR_BAR 0x02 /* Enable Color Bar Test */
+#define COM7_GET_RES(x) ((x)&0x70)
+
+#define COM8 0x13
+#define COM8_DEFAULT 0xC0
+#define COM8_BNDF_EN 0x20 /* Enable Banding filter */
+#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
+#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
+#define COM8_SET(x) (COM8_DEFAULT|x)
+#define COM8_SET_AEC(r,x) (((r)&0xFE)|((x)&1))
+
+#define COM9 0x14 /* AGC gain ceiling */
+#define COM9_DEFAULT 0x08
+#define COM9_AGC_GAIN_2x 0x00 /* AGC: 2x */
+#define COM9_AGC_GAIN_4x 0x01 /* AGC: 4x */
+#define COM9_AGC_GAIN_8x 0x02 /* AGC: 8x */
+#define COM9_AGC_GAIN_16x 0x03 /* AGC: 16x */
+#define COM9_AGC_GAIN_32x 0x04 /* AGC: 32x */
+#define COM9_AGC_GAIN_64x 0x05 /* AGC: 64x */
+#define COM9_AGC_GAIN_128x 0x06 /* AGC: 128x */
+#define COM9_AGC_SET(x) (COM9_DEFAULT|(x<<5))
+
+#define CTRL1_AWB 0x08 /* Enable AWB */
+
+#define VV 0x26
+#define VV_AGC_TH_SET(h,l) ((h<<4)|(l&0x0F))
+
+#define REG32 0x32
+#define REG32_UXGA 0x36
+#define REG32_SVGA 0x09
+#define REG32_CIF 0x00
+
+#define VAL_SET(x, mask, rshift, lshift) ((((x) >> rshift) & mask) << lshift)
+
+#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3)
+#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0)
+
+#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6)
+#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3)
+#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0)
+
+#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
+#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
+
+#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
+
+#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
+#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
+
+#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7)
+#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3)
+#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4)
+#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0)
+
+#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7)
+
+#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0)
+
+#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4)
+#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2)
+#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0)
+
+#endif //__REG_REGS_H__
diff --git a/fw/fe310/eos/dev/sdc_crypto.c b/fw/fe310/eos/dev/sdc_crypto.c
new file mode 100644
index 0000000..f0e935d
--- /dev/null
+++ b/fw/fe310/eos/dev/sdc_crypto.c
@@ -0,0 +1,51 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "sha/sha1.h"
+#include "sdc_crypto.h"
+
+#define SDC_CRYPTO_KEY_SIZE 16
+#define SDC_CRYPTO_BLK_SIZE 16
+#define SDC_CRYPTO_HASH_SIZE 20
+
+EOSSDCCrypto *sdc_crypto;
+
+void eos_sdcc_init(EOSSDCCrypto *crypto, uint8_t *key, void *ctx, eve_sdcc_init_t init, eve_sdcc_crypt_t enc, eve_sdcc_crypt_t dec, void *ctx_essiv, eve_sdcc_init_t init_essiv, eve_sdcc_essiv_t enc_essiv) {
+ char key_essiv[SDC_CRYPTO_HASH_SIZE];
+
+ sdc_crypto = crypto;
+
+ memset(key_essiv, 0, SDC_CRYPTO_HASH_SIZE);
+ init(ctx, key);
+ sdc_crypto->ctx = ctx;
+ sdc_crypto->enc = enc;
+ sdc_crypto->dec = dec;
+
+ SHA1(key_essiv, key, SDC_CRYPTO_KEY_SIZE);
+ init_essiv(ctx_essiv, key_essiv);
+ sdc_crypto->ctx_essiv = ctx_essiv;
+ sdc_crypto->enc_essiv = enc_essiv;
+}
+
+void eos_sdcc_encrypt(uint32_t sect, uint8_t *buffer) {
+ uint8_t iv[SDC_CRYPTO_BLK_SIZE];
+
+ if (sdc_crypto == NULL) return;
+
+ memset(iv, 0, SDC_CRYPTO_BLK_SIZE);
+ memcpy(iv, &sect, sizeof(sect));
+ sdc_crypto->enc_essiv(sdc_crypto->ctx_essiv, iv);
+ sdc_crypto->enc(sdc_crypto->ctx, iv, buffer, 512);
+}
+
+void eos_sdcc_decrypt(uint32_t sect, uint8_t *buffer) {
+ uint8_t iv[SDC_CRYPTO_BLK_SIZE];
+
+ if (sdc_crypto == NULL) return;
+
+ memset(iv, 0, SDC_CRYPTO_BLK_SIZE);
+ memcpy(iv, &sect, sizeof(sect));
+ sdc_crypto->enc_essiv(sdc_crypto->ctx_essiv, iv);
+ sdc_crypto->dec(sdc_crypto->ctx, iv, buffer, 512);
+}
diff --git a/fw/fe310/eos/dev/sdc_crypto.h b/fw/fe310/eos/dev/sdc_crypto.h
new file mode 100644
index 0000000..6442547
--- /dev/null
+++ b/fw/fe310/eos/dev/sdc_crypto.h
@@ -0,0 +1,18 @@
+#include <stddef.h>
+#include <stdint.h>
+
+typedef void (*eve_sdcc_init_t) (void *, uint8_t *);
+typedef void (*eve_sdcc_crypt_t) (void *, uint8_t *, uint8_t *, size_t);
+typedef void (*eve_sdcc_essiv_t) (void *, uint8_t *);
+
+typedef struct EOSSDCCrypto {
+ void *ctx;
+ eve_sdcc_crypt_t enc;
+ eve_sdcc_crypt_t dec;
+ void *ctx_essiv;
+ eve_sdcc_essiv_t enc_essiv;
+} EOSSDCCrypto;
+
+void eos_sdcc_init(EOSSDCCrypto *crypto, uint8_t *key, void *ctx, eve_sdcc_init_t init, eve_sdcc_crypt_t enc, eve_sdcc_crypt_t dec, void *ctx_essiv, eve_sdcc_init_t init_essiv, eve_sdcc_essiv_t enc_essiv);
+void eos_sdcc_encrypt(uint32_t sect, uint8_t *buffer);
+void eos_sdcc_decrypt(uint32_t sect, uint8_t *buffer);
diff --git a/fw/fe310/eos/dev/sdcard.c b/fw/fe310/eos/dev/sdcard.c
new file mode 100644
index 0000000..fa316c7
--- /dev/null
+++ b/fw/fe310/eos/dev/sdcard.c
@@ -0,0 +1,539 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "eos.h"
+#include "soc/timer.h"
+#include "soc/spi.h"
+
+#include "spi.h"
+
+#include "sdc_crypto.h"
+#include "sdcard.h"
+
+#define SDC_TIMEOUT_CMD 500
+#define SDC_TIMEOUT_READ 200
+#define SDC_TIMEOUT_WRITE 500
+
+#define SDC_POLY_CRC7 0x09
+#define SDC_POLY_CRC16 0x1021
+
+#define SDC_CMD_FLAG_CRC 0x01
+#define SDC_CMD_FLAG_NOCS 0x02
+#define SDC_CMD_FLAG_NOWAIT 0x04
+#define SDC_CMD_FLAG_RSTUFF 0x08
+
+#define SDC_TOKEN_START_BLK 0xfe
+#define SDC_TOKEN_START_BLKM 0xfc
+#define SDC_TOKEN_STOP_TRAN 0xfd
+
+#define SDC_DRESP_MASK 0x1f
+#define SDC_DRESP_ACCEPT 0x05
+#define SDC_DRESP_ERR_CRC 0x0b
+#define SDC_DRESP_ERR_WRITE 0x0d
+
+#define SDC_R1_READY 0x00
+
+#define SDC_R1_IDLE_STATE 0x01
+#define SDC_R1_ERASE_RESET 0x02
+#define SDC_R1_ILLEGAL_CMD 0x04
+#define SDC_R1_ERR_CMD_CRC 0x08
+#define SDC_R1_ERR_ERASE_SEQ 0x10
+#define SDC_R1_ERR_ADDR 0x20
+#define SDC_R1_ERR_PARAM 0x40
+
+#define SDC_NCR 10
+
+#define SDC_ERR(rv) ((rv < 0) ? rv : EOS_ERR)
+
+/* CMD */
+#define GO_IDLE_STATE 0
+#define SEND_OP_COND 1
+#define SEND_IF_COND 8
+#define SEND_CSD 9
+#define STOP_TRANSMISSION 12
+#define SET_BLOCKLEN 16
+#define READ_SINGLE_BLOCK 17
+#define READ_MULTIPLE_BLOCK 18
+#define WRITE_BLOCK 24
+#define WRITE_MULTIPLE_BLOCK 25
+#define ERASE_WR_BLK_START 32
+#define ERASE_WR_BLK_END 33
+#define ERASE 38
+#define APP_CMD 55
+#define READ_OCR 58
+
+/* ACMD */
+#define SD_STATUS 13
+#define SET_WR_BLK_ERASE_COUNT 23
+#define SD_APP_OP_COND 41
+
+static uint8_t sdc_type = 0;
+
+static uint8_t sdc_crc7(uint8_t crc, uint8_t b) {
+ int i;
+
+ for (i=8; i--; b<<=1) {
+ crc <<= 1;
+ if ((b ^ crc) & 0x80) crc ^= SDC_POLY_CRC7;
+ }
+ return crc & 0x7f;
+}
+
+static uint16_t sdc_crc16(uint16_t crc, uint8_t b) {
+ int i;
+
+ crc = crc ^ ((uint16_t)b << 8);
+ for (i=8; i--;) {
+ if (crc & 0x8000) {
+ crc = (crc << 1) ^ SDC_POLY_CRC16;
+ } else {
+ crc <<= 1;
+ }
+ }
+ return crc;
+}
+
+static uint32_t sdc_nto(uint32_t start, uint32_t timeout) {
+ uint32_t d = eos_time_delta_ms(start);
+ return (d > timeout) ? 0 : timeout - d;
+}
+
+static uint8_t sdc_xchg8(uint8_t data) {
+ return eos_spi_xchg8(data, 0);
+}
+
+static uint16_t sdc_xchg16(uint16_t data) {
+ return eos_spi_xchg16(data, 0);
+}
+
+static uint32_t sdc_xchg32(uint32_t data) {
+ return eos_spi_xchg32(data, 0);
+}
+
+static void sdc_buf_send(unsigned char *buffer, uint16_t len) {
+ int i;
+
+ for (i=0; i<len; i++) {
+ sdc_xchg8(buffer[i]);
+ }
+}
+
+static void sdc_buf_recv(unsigned char *buffer, uint16_t len) {
+ int i;
+
+ for (i=0; i<len; i++) {
+ buffer[i] = sdc_xchg8(0xff);
+ }
+}
+
+static void sdc_select(void) {
+ eos_spi_cs_set();
+ eos_spi_xchg8(0xff, 0);
+}
+
+static void sdc_deselect(void) {
+ eos_spi_cs_clear();
+ eos_spi_xchg8(0xff, 0);
+}
+
+static int sdc_xchg_cmd(uint8_t cmd, uint32_t arg, uint8_t flags) {
+ int i;
+ uint8_t ret;
+ uint8_t crc = 0x7f;
+
+ cmd |= 0x40;
+ if (flags & SDC_CMD_FLAG_CRC) {
+ crc = sdc_crc7(0, cmd);
+ crc = sdc_crc7(crc, arg >> 24);
+ crc = sdc_crc7(crc, arg >> 16);
+ crc = sdc_crc7(crc, arg >> 8);
+ crc = sdc_crc7(crc, arg);
+ }
+ crc = (crc << 1) | 0x01;
+ sdc_xchg8(cmd);
+ sdc_xchg32(arg);
+ sdc_xchg8(crc);
+ if (flags & SDC_CMD_FLAG_RSTUFF) sdc_xchg8(0xff);
+
+ i = SDC_NCR;
+ do {
+ ret = sdc_xchg8(0xff);
+ } while ((ret & 0x80) && --i);
+ if (ret & 0x80) return EOS_ERR_BUSY;
+
+ return ret;
+}
+
+static int sdc_ready(uint32_t timeout) {
+ uint8_t d = 0;
+ uint32_t start;
+
+ if (timeout == 0) return EOS_ERR_BUSY;
+ start = eos_time_get_tick();
+ do {
+ if (eos_time_delta_ms(start) > timeout) break;
+ d = sdc_xchg8(0xff);
+ } while (d != 0xff);
+ if (d != 0xff) return EOS_ERR_BUSY;
+
+ return EOS_OK;
+}
+
+static int sdc_block_read(uint8_t *buffer, uint16_t len, uint32_t timeout) {
+ uint8_t token = 0xff;
+ uint32_t start;
+
+ if (timeout == 0) return EOS_ERR_BUSY;
+ start = eos_time_get_tick();
+ do {
+ if (eos_time_delta_ms(start) > timeout) break;
+ token = sdc_xchg8(0xff);
+ } while (token == 0xff);
+ if (token == 0xff) return EOS_ERR_BUSY;
+ if (token != SDC_TOKEN_START_BLK) return EOS_ERR;
+
+ sdc_buf_recv(buffer, len);
+ sdc_xchg16(0xffff); /* dummy CRC */
+
+ return EOS_OK;
+}
+
+static int sdc_block_write(uint8_t token, uint8_t *buffer, uint16_t len, uint32_t timeout) {
+ uint8_t d;
+ int rv;
+
+ rv = sdc_ready(timeout);
+ if (rv) return rv;
+
+ sdc_xchg8(token);
+ if (buffer && len) {
+ sdc_buf_send(buffer, len);
+ sdc_xchg16(0xffff); /* dummy CRC */
+
+ d = sdc_xchg8(0xff); /* Response */
+ if ((d & SDC_DRESP_MASK) != SDC_DRESP_ACCEPT) return EOS_ERR;
+ }
+
+ return EOS_OK;
+}
+
+static int sdc_cmd(uint8_t cmd, uint32_t arg, uint8_t flags, uint32_t timeout) {
+ int do_cs = !(flags & SDC_CMD_FLAG_NOCS);
+ int do_wait = !(flags & SDC_CMD_FLAG_NOWAIT);
+ int rv = EOS_OK;
+
+ if (do_cs) sdc_select();
+ if (do_wait) rv = sdc_ready(timeout);
+ if (rv) {
+ if (do_cs) sdc_deselect();
+ return rv;
+ }
+ rv = sdc_xchg_cmd(cmd, arg, flags);
+ if (do_cs) sdc_deselect();
+ return rv;
+}
+
+static int sdc_acmd(uint8_t cmd, uint32_t arg, uint8_t flags, uint32_t timeout) {
+ int rv;
+ uint32_t start;
+
+ start = eos_time_get_tick();
+ rv = sdc_cmd(APP_CMD, 0, flags, timeout);
+ if (rv & ~SDC_R1_IDLE_STATE) return rv;
+
+ if (flags & SDC_CMD_FLAG_NOCS) {
+ sdc_deselect();
+ sdc_select();
+ }
+ return sdc_cmd(cmd, arg, flags, sdc_nto(start, timeout));
+}
+
+static int sdc_init(uint32_t timeout) {
+ int rv, i;
+ uint8_t _type;
+ uint8_t ocr[4];
+ uint32_t start;
+ start = eos_time_get_tick();
+
+ eos_time_sleep(100);
+ for (i=10; i--;) sdc_xchg8(0xff); /* 80 dummy cycles */
+
+ rv = sdc_cmd(GO_IDLE_STATE, 0, SDC_CMD_FLAG_CRC, SDC_TIMEOUT_CMD);
+ if (rv != SDC_R1_IDLE_STATE) return SDC_ERR(rv);
+
+ sdc_select();
+ rv = sdc_cmd(SEND_IF_COND, 0x1aa, SDC_CMD_FLAG_CRC | SDC_CMD_FLAG_NOCS, sdc_nto(start, timeout));
+ switch (rv) {
+ case SDC_R1_IDLE_STATE:
+ for (i=0; i<4; i++) { /* R7 response */
+ ocr[i] = sdc_xchg8(0xff);
+ }
+ sdc_deselect();
+ if (ocr[2] == 0x01 && ocr[3] == 0xaa) { /* Check voltage range: 2.7-3.6V */
+ do {
+ rv = sdc_acmd(SD_APP_OP_COND, 0x40000000, 0, sdc_nto(start, timeout));
+ } while (rv == SDC_R1_IDLE_STATE);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+
+ sdc_select();
+ rv = sdc_cmd(READ_OCR, 0, SDC_CMD_FLAG_NOCS, sdc_nto(start, timeout));
+ if (rv == SDC_R1_READY) {
+ for (i=0; i<4; i++) { /* R7 response */
+ ocr[i] = sdc_xchg8(0xff);
+ }
+ sdc_deselect();
+ } else {
+ sdc_deselect();
+ return SDC_ERR(rv);
+ }
+
+ _type = EOS_SDC_TYPE_SDC2;
+ if (ocr[0] & 0xc0) _type |= EOS_SDC_CAP_BLK;
+ }
+ break;
+
+ case SDC_R1_IDLE_STATE | SDC_R1_ILLEGAL_CMD:
+ sdc_deselect();
+ rv = sdc_acmd(SD_APP_OP_COND, 0, 0, sdc_nto(start, timeout));
+ switch (rv) {
+ case SDC_R1_IDLE_STATE:
+ do {
+ rv = sdc_acmd(SD_APP_OP_COND, 0, 0, sdc_nto(start, timeout));
+ } while (rv == SDC_R1_IDLE_STATE);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+ case SDC_R1_READY:
+ _type = EOS_SDC_TYPE_SDC1;
+ break;
+
+ default:
+ do {
+ rv = sdc_cmd(SEND_OP_COND, 0, 0, sdc_nto(start, timeout));
+ } while (rv == SDC_R1_IDLE_STATE);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+ _type = EOS_SDC_TYPE_MMC;
+ break;
+
+ }
+ break;
+
+ default:
+ sdc_deselect();
+ return SDC_ERR(rv);
+ }
+
+ if (!(_type & EOS_SDC_CAP_BLK)) {
+ rv = sdc_cmd(SET_BLOCKLEN, 512, 0, sdc_nto(start, timeout));
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+ }
+
+ if (_type & EOS_SDC_TYPE_SDC) {
+ uint8_t csd[16];
+
+ sdc_select();
+ rv = sdc_cmd(SEND_CSD, 0, SDC_CMD_FLAG_NOCS, sdc_nto(start, timeout));
+ if (rv == SDC_R1_READY) {
+ rv = sdc_block_read(csd, 16, sdc_nto(start, timeout));
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ if (rv) return rv;
+
+ for (i=0; i<16; i++) {
+ printf("%.2x ", csd[i]);
+ }
+ printf("\n");
+ if (csd[10] & 0x40) _type |= EOS_SDC_CAP_ERASE_EN;
+ }
+
+ eos_spi_set_div(EOS_SPI_DEV_SDC, 5);
+ sdc_type = _type;
+ return EOS_OK;
+}
+
+int eos_sdc_init(uint8_t wakeup_cause) {
+ int rv;
+
+ eos_spi_select(EOS_SPI_DEV_SDC);
+ rv = sdc_init(5000);
+ if (rv) {
+ printf("SDC ERROR\n");
+ } else {
+ printf("SDC OK:%x\n", sdc_type);
+ }
+ eos_spi_deselect();
+
+ return EOS_OK;
+}
+
+uint8_t eos_sdc_type(void) {
+ return sdc_type & EOS_SDC_TYPE_MASK;
+}
+
+uint8_t eos_sdc_cap(void) {
+ return sdc_type & EOS_SDC_CAP_MASK;
+}
+
+int eos_sdc_get_sect_count(uint32_t timeout, uint32_t *sectors) {
+ int rv;
+ uint8_t csd[16];
+ uint32_t start = eos_time_get_tick();
+
+ sdc_select();
+ rv = sdc_cmd(SEND_CSD, 0, SDC_CMD_FLAG_NOCS, timeout);
+ if (rv == SDC_R1_READY) {
+ rv = sdc_block_read(csd, 16, sdc_nto(start, timeout));
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ if (rv) return rv;
+
+ if ((csd[0] >> 6) == 1) { /* SDCv2 */
+ *sectors = (csd[9] + (csd[8] << 8) + ((csd[7] & 0x3f) << 16) + 1) << 10;
+ } else { /* SDCv1 or MMC*/
+ uint8_t n = (csd[5] & 0x0f) + (csd[10] >> 7) + ((csd[9] & 0x03) << 1) + 2;
+ *sectors = (csd[8] >> 6) + (csd[7] << 2) + ((csd[6] & 0x03) << 10) + 1;
+ *sectors = *sectors << (n - 9);
+ }
+
+ return EOS_OK;
+}
+
+int eos_sdc_get_blk_size(uint32_t timeout, uint32_t *size) {
+ int rv;
+ uint8_t rbl[64]; /* SD Status or CSD register */
+ uint32_t start = eos_time_get_tick();
+
+ sdc_select();
+ if (sdc_type & EOS_SDC_TYPE_SDC2) {
+ rv = sdc_acmd(SD_STATUS, 0, SDC_CMD_FLAG_NOCS, timeout);
+ sdc_xchg8(0xff); /* R2 response */
+ } else {
+ rv = sdc_cmd(SEND_CSD, 0, SDC_CMD_FLAG_NOCS, timeout);
+ }
+ if (rv == SDC_R1_READY) {
+ rv = sdc_block_read(rbl, (sdc_type & EOS_SDC_TYPE_SDC2) ? 64 : 16, sdc_nto(start, timeout));
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ if (rv) return rv;
+
+ if (sdc_type & EOS_SDC_TYPE_SDC2) {
+ *size = 16UL << (rbl[10] >> 4);
+ } else if (sdc_type & EOS_SDC_TYPE_SDC1) {
+ *size = (((rbl[10] & 0x3f) << 1) + (rbl[11] >> 7) + 1) << ((rbl[13] >> 6) - 1);
+ } else {
+ *size = (((rbl[10] & 0x7c) >> 2) + 1) * (((rbl[11] & 0x03) << 3) + (rbl[11] >> 5) + 1);
+ }
+
+ return EOS_OK;
+}
+
+int eos_sdc_sync(uint32_t timeout) {
+ int rv;
+
+ sdc_select();
+ rv = sdc_ready(timeout);
+ sdc_deselect();
+
+ return rv;
+}
+
+int eos_sdc_erase(uint32_t blk_start, uint32_t blk_end, uint32_t timeout) {
+ int rv;
+ uint32_t start;
+
+ if (!(sdc_type & EOS_SDC_TYPE_SDC)) return EOS_ERR;
+ if (!(sdc_type & EOS_SDC_CAP_ERASE_EN)) return EOS_ERR;
+ if (!(sdc_type & EOS_SDC_CAP_BLK)) {
+ blk_start *= 512;
+ blk_end *= 512;
+ }
+
+ start = eos_time_get_tick();
+ rv = sdc_cmd(ERASE_WR_BLK_START, blk_start, 0, timeout);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+
+ rv = sdc_cmd(ERASE_WR_BLK_END, blk_end, 0, sdc_nto(start, timeout));
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+
+ rv = sdc_cmd(ERASE, 0, 0, sdc_nto(start, timeout));
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+
+ return eos_sdc_sync(sdc_nto(start, timeout));
+}
+
+int eos_sdc_sect_read(uint32_t sect, unsigned int count, uint8_t *buffer) {
+ int rv;
+ uint8_t cmd = ((count == 1) ? READ_SINGLE_BLOCK : READ_MULTIPLE_BLOCK);
+
+ if (!(sdc_type & EOS_SDC_CAP_BLK)) sect *= 512;
+
+ sdc_select();
+ rv = sdc_cmd(cmd, sect, SDC_CMD_FLAG_NOCS, SDC_TIMEOUT_CMD);
+ if (rv == SDC_R1_READY) {
+ int _rv = SDC_R1_READY;
+
+ while (count) {
+ rv = sdc_block_read(buffer, 512, SDC_TIMEOUT_READ);
+ if (rv) break;
+ eos_sdcc_decrypt(sect, buffer);
+ buffer += 512;
+ count--;
+ }
+ if (cmd == READ_MULTIPLE_BLOCK) _rv = sdc_cmd(STOP_TRANSMISSION, 0, SDC_CMD_FLAG_NOCS | SDC_CMD_FLAG_NOWAIT | SDC_CMD_FLAG_RSTUFF, 0);
+ if (!rv && (_rv != SDC_R1_READY)) rv = SDC_ERR(_rv);
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+
+ return rv;
+}
+
+int eos_sdc_sect_write(uint32_t sect, unsigned int count, uint8_t *buffer) {
+ int rv;
+
+ if (!(sdc_type & EOS_SDC_CAP_BLK)) sect *= 512;
+
+ if (count == 1) {
+ sdc_select();
+ rv = sdc_cmd(WRITE_BLOCK, sect, SDC_CMD_FLAG_NOCS, SDC_TIMEOUT_CMD);
+ if (rv == SDC_R1_READY) {
+ eos_sdcc_encrypt(sect, buffer);
+ rv = sdc_block_write(SDC_TOKEN_START_BLK, buffer, 512, SDC_TIMEOUT_WRITE);
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ } else {
+ if (sdc_type & EOS_SDC_TYPE_SDC) {
+ rv = sdc_acmd(SET_WR_BLK_ERASE_COUNT, count, 0, SDC_TIMEOUT_CMD);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+ }
+
+ sdc_select();
+ rv = sdc_cmd(WRITE_MULTIPLE_BLOCK, sect, SDC_CMD_FLAG_NOCS, SDC_TIMEOUT_CMD);
+ if (rv == SDC_R1_READY) {
+ int _rv;
+
+ while (count) {
+ eos_sdcc_encrypt(sect, buffer);
+ rv = sdc_block_write(SDC_TOKEN_START_BLKM, buffer, 512, SDC_TIMEOUT_WRITE);
+ if (rv) break;
+ buffer += 512;
+ count--;
+ }
+ _rv = sdc_block_write(SDC_TOKEN_STOP_TRAN, NULL, 0, SDC_TIMEOUT_WRITE);
+ if (!rv && (_rv != SDC_R1_READY)) rv = SDC_ERR(_rv);
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ }
+
+ return rv;
+}
diff --git a/fw/fe310/eos/dev/sdcard.h b/fw/fe310/eos/dev/sdcard.h
new file mode 100644
index 0000000..910a6e0
--- /dev/null
+++ b/fw/fe310/eos/dev/sdcard.h
@@ -0,0 +1,22 @@
+#include <stdint.h>
+
+#define EOS_SDC_TYPE_MMC 0x01
+
+#define EOS_SDC_TYPE_SDC1 0x04
+#define EOS_SDC_TYPE_SDC2 0x08
+#define EOS_SDC_TYPE_SDC 0x0c
+#define EOS_SDC_TYPE_MASK 0x0f
+
+#define EOS_SDC_CAP_BLK 0x10
+#define EOS_SDC_CAP_ERASE_EN 0x20
+#define EOS_SDC_CAP_MASK 0xf0
+
+int eos_sdc_init(uint8_t wakeup_cause);
+uint8_t eos_sdc_type(void);
+uint8_t eos_sdc_cap(void);
+int eos_sdc_get_sect_count(uint32_t timeout, uint32_t *sectors);
+int eos_sdc_get_blk_size(uint32_t timeout, uint32_t *size);
+int eos_sdc_sync(uint32_t timeout);
+int eos_sdc_erase(uint32_t blk_start, uint32_t blk_end, uint32_t timeout);
+int eos_sdc_sect_read(uint32_t sect, unsigned int count, uint8_t *buffer);
+int eos_sdc_sect_write(uint32_t sect, unsigned int count, uint8_t *buffer);
diff --git a/fw/fe310/eos/dev/spi.c b/fw/fe310/eos/dev/spi.c
new file mode 100644
index 0000000..12549fc
--- /dev/null
+++ b/fw/fe310/eos/dev/spi.c
@@ -0,0 +1,113 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "eos.h"
+#include "msgq.h"
+#include "event.h"
+
+#include "board.h"
+
+#include "soc/interrupt.h"
+#include "soc/spi.h"
+
+#include "net.h"
+
+#include "spi.h"
+#include "spi_cfg.h"
+
+static uint8_t spi_dev;
+static uint8_t spi_lock;
+static uint16_t spi_div[EOS_SPI_MAX_DEV];
+
+int eos_spi_dev_init(uint8_t wakeup_cause) {
+ int i;
+
+ for (i=0; i<EOS_SPI_MAX_DEV; i++) {
+ spi_div[i] = spi_cfg[i].div;
+ if (!(spi_cfg[i].flags & SPI_DEV_FLAG_CSFLOAT) && (spi_cfg[i].cspin != -1)) {
+ GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << spi_cfg[i].cspin);
+ GPIO_REG(GPIO_OUTPUT_EN) |= (1 << spi_cfg[i].cspin);
+ }
+ }
+
+ return EOS_OK;
+}
+
+int eos_spi_select(unsigned char dev) {
+ if (spi_lock) return EOS_ERR_BUSY;
+
+ if (spi_cfg[spi_dev].flags & SPI_DEV_FLAG_9BIT) {
+ eos_spi_enable();
+ } else {
+ if (spi_dev == EOS_SPI_DEV_NET) {
+ eos_net_stop();
+ } else {
+ eos_spi_stop();
+ }
+ }
+
+ spi_dev = dev;
+ if (spi_cfg[dev].flags & SPI_DEV_FLAG_9BIT) {
+ eos_spi_configure(spi_div[dev], spi_cfg[dev].csid, spi_cfg[dev].cspin, spi_cfg[dev].evt);
+ eos_spi_disable();
+ } else {
+ if (dev == EOS_SPI_DEV_NET) {
+ eos_net_start();
+ } else {
+ eos_spi_start(spi_div[dev], spi_cfg[dev].csid, spi_cfg[dev].cspin, spi_cfg[dev].evt);
+ }
+ }
+
+ return EOS_OK;
+}
+
+int eos_spi_deselect(void) {
+ if (spi_lock) return EOS_ERR_BUSY;
+ if (spi_dev == EOS_SPI_DEV_NET) return EOS_ERR;
+
+ if (spi_cfg[spi_dev].flags & SPI_DEV_FLAG_9BIT) {
+ eos_spi_enable();
+ } else {
+ eos_spi_stop();
+ }
+
+ spi_dev = EOS_SPI_DEV_NET;
+ eos_net_start();
+
+ return EOS_OK;
+}
+
+void eos_spi_dev_configure(unsigned char dev) {
+ eos_spi_configure(spi_div[dev], spi_cfg[dev].csid, spi_cfg[dev].cspin, spi_cfg[dev].evt);
+}
+
+uint8_t eos_spi_dev(void) {
+ return spi_dev;
+}
+
+uint16_t eos_spi_div(unsigned char dev) {
+ return spi_div[dev];
+}
+
+uint8_t eos_spi_csid(unsigned char dev) {
+ return spi_cfg[dev].csid;
+}
+
+uint8_t eos_spi_cspin(unsigned char dev) {
+ return spi_cfg[dev].cspin;
+}
+
+void eos_spi_lock(void) {
+ spi_lock = 1;
+}
+
+void eos_spi_unlock(void) {
+ spi_lock = 0;
+}
+
+void eos_spi_set_div(unsigned char dev, uint16_t div) {
+ spi_div[dev] = div;
+}
diff --git a/fw/fe310/eos/dev/spi.h b/fw/fe310/eos/dev/spi.h
new file mode 100644
index 0000000..c899e83
--- /dev/null
+++ b/fw/fe310/eos/dev/spi.h
@@ -0,0 +1,21 @@
+#include <stdint.h>
+
+#define EOS_SPI_DEV_NET 0
+#define EOS_SPI_DEV_EVE 1
+#define EOS_SPI_DEV_SDC 2
+#define EOS_SPI_DEV_CAM 3
+#define EOS_SPI_DEV_LCD 4
+
+int eos_spi_dev_init(uint8_t wakeup_cause);
+int eos_spi_select(unsigned char dev);
+int eos_spi_deselect(void);
+void eos_spi_dev_configure(unsigned char dev);
+
+uint8_t eos_spi_dev(void);
+uint16_t eos_spi_div(unsigned char dev);
+uint8_t eos_spi_csid(unsigned char dev);
+uint8_t eos_spi_cspin(unsigned char dev);
+
+void eos_spi_lock(void);
+void eos_spi_unlock(void);
+void eos_spi_set_div(unsigned char dev, uint16_t div);
diff --git a/fw/fe310/eos/dev/spi_cfg.h b/fw/fe310/eos/dev/spi_cfg.h
new file mode 100644
index 0000000..6a5d7b4
--- /dev/null
+++ b/fw/fe310/eos/dev/spi_cfg.h
@@ -0,0 +1,52 @@
+#include <stdint.h>
+
+#define EOS_SPI_MAX_DEV 5
+
+typedef struct {
+ uint16_t div;
+ int8_t csid;
+ int8_t cspin;
+ uint8_t flags;
+ unsigned char evt;
+} SPIConfig;
+
+#define SPI_DEV_FLAG_9BIT 0x1
+#define SPI_DEV_FLAG_CSFLOAT 0x2
+
+static const SPIConfig spi_cfg[EOS_SPI_MAX_DEV] = {
+ { // DEV_NET
+ .div = SPI_DIV_NET,
+ .csid = SPI_CSID_NET,
+ .cspin = SPI_CSPIN_NET,
+ .flags = 0,
+ .evt = 0, // Not SPI event
+ },
+ { // DEV_EVE
+ .div = SPI_DIV_EVE,
+ .csid = SPI_CSID_EVE,
+ .cspin = SPI_CSPIN_EVE,
+ .flags = 0,
+ .evt = 0,
+ },
+ { // DEV_SDC
+ .div = SPI_DIV_SDC,
+ .csid = SPI_CSID_SDC,
+ .cspin = SPI_CSPIN_SDC,
+ .flags = 0,
+ .evt = EOS_SPI_EVT_SDC,
+ },
+ { // DEV_CAM
+ .div = SPI_DIV_CAM,
+ .csid = SPI_CSID_CAM,
+ .cspin = SPI_CSPIN_CAM,
+ .flags = 0,
+ .evt = EOS_SPI_EVT_CAM,
+ },
+ { // DEV_LCD 9bit spi
+ .div = 0,
+ .csid = -1,
+ .cspin = SPI_CSPIN_LCD,
+ .flags = SPI_DEV_FLAG_9BIT | SPI_DEV_FLAG_CSFLOAT,
+ .evt = 0,
+ },
+};