icE1usb fw: Import I2C peripheral driver
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Change-Id: Ib729bb5f4e94eec25c86517042cdfdcb6847ba25
diff --git a/firmware/ice40-riscv/icE1usb/Makefile b/firmware/ice40-riscv/icE1usb/Makefile
index 3fde2ca..f385408 100644
--- a/firmware/ice40-riscv/icE1usb/Makefile
+++ b/firmware/ice40-riscv/icE1usb/Makefile
@@ -55,6 +55,7 @@
gps.h \
gpsdo.h \
ice1usb_proto.h \
+ i2c.h \
misc.h \
usb_desc_ids.h \
usb_dev.h \
@@ -69,6 +70,7 @@
fw_app.c \
gps.c \
gpsdo.c \
+ i2c.c \
misc.c \
usb_desc_app.c \
usb_dev.c \
diff --git a/firmware/ice40-riscv/icE1usb/i2c.c b/firmware/ice40-riscv/icE1usb/i2c.c
new file mode 100644
index 0000000..d3a9378
--- /dev/null
+++ b/firmware/ice40-riscv/icE1usb/i2c.c
@@ -0,0 +1,125 @@
+/*
+ * i2c.c
+ *
+ * Copyright (C) 2021-2022 Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "console.h"
+
+#include "config.h"
+
+
+struct i2c {
+ uint32_t csr;
+} __attribute__((packed,aligned(4)));
+
+#define I2C_CMD_START (0 << 12)
+#define I2C_CMD_STOP (1 << 12)
+#define I2C_CMD_WRITE (2 << 12)
+#define I2C_CMD_READ (3 << 12)
+
+#define I2C_GET_RESP (1 << 15)
+#define I2C_ACK (0 << 8) /* ack bit value = 0 means ACK */
+#define I2C_NAK (1 << 8) /* ack bit value = 1 means NAK */
+
+#define I2C_VALID (1 << 31)
+
+
+static volatile struct i2c * const i2c_regs = (void *)(I2C_BASE);
+
+
+static inline uint32_t
+_i2c_wait(void)
+{
+ uint32_t v;
+
+ do {
+ v = i2c_regs->csr;
+ } while (!(v & I2C_VALID));
+
+ return v & 0x1ff;
+}
+
+bool
+i2c_ready(void)
+{
+ return i2c_regs->csr & (1 << 31);
+}
+
+void
+i2c_start(void)
+{
+ i2c_regs->csr = I2C_CMD_START;
+}
+
+void
+i2c_stop(void)
+{
+ i2c_regs->csr = I2C_CMD_STOP;
+}
+
+bool
+i2c_write(uint8_t data)
+{
+ i2c_regs->csr = I2C_CMD_WRITE | data;
+ return (_i2c_wait() & (I2C_ACK | I2C_NAK)) == I2C_ACK;
+}
+
+uint8_t
+i2c_read(bool ack)
+{
+ i2c_regs->csr = I2C_CMD_READ | I2C_GET_RESP | (ack ? I2C_ACK : I2C_NAK);
+ return _i2c_wait() & 0xff;
+}
+
+
+bool
+i2c_write_reg(uint8_t dev, uint8_t reg, uint8_t val)
+{
+ bool rv = true;
+ i2c_start();
+ rv = rv && i2c_write(dev);
+ rv = rv && i2c_write(reg);
+ rv = rv && i2c_write(val);
+ i2c_stop();
+ return rv;
+}
+
+bool
+i2c_read_reg(uint8_t dev, uint8_t reg, uint8_t *val)
+{
+ bool rv = true;
+ i2c_start();
+ rv = rv && i2c_write(dev);
+ rv = rv && i2c_write(reg);
+ if (rv)
+ i2c_start();
+ rv = rv && i2c_write(dev|1);
+ *val = rv ? i2c_read(false) : 0x00; // NAK
+ i2c_stop();
+ return rv;
+}
+
+
+bool
+i2c_probe(uint8_t dev)
+{
+ bool rv;
+ i2c_start();
+ rv = i2c_write(dev);
+ i2c_stop();
+ return rv;
+}
+
+void
+i2c_scan(void)
+{
+ for (uint8_t addr=0; addr<128; addr++) {
+ if (i2c_probe(addr << 1))
+ printf("I2C @ %08x\n", addr << 1);
+ }
+}
diff --git a/firmware/ice40-riscv/icE1usb/i2c.h b/firmware/ice40-riscv/icE1usb/i2c.h
new file mode 100644
index 0000000..da11eb5
--- /dev/null
+++ b/firmware/ice40-riscv/icE1usb/i2c.h
@@ -0,0 +1,22 @@
+/*
+ * i2c.h
+ *
+ * Copyright (C) 2021-2022 Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+void i2c_start(void);
+void i2c_stop(void);
+bool i2c_write(uint8_t data);
+uint8_t i2c_read(bool ack);
+
+bool i2c_write_reg(uint8_t dev, uint8_t reg, uint8_t val);
+bool i2c_read_reg (uint8_t dev, uint8_t reg, uint8_t *val);
+
+bool i2c_probe(uint8_t dev);
+void i2c_scan(void);