firmware/ice40-riscv: Import common parts to all iCE40/RISC-V firmwares
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
diff --git a/firmware/ice40-riscv/common/led.c b/firmware/ice40-riscv/common/led.c
new file mode 100644
index 0000000..3ae298a
--- /dev/null
+++ b/firmware/ice40-riscv/common/led.c
@@ -0,0 +1,167 @@
+/*
+ * led.c
+ *
+ * Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "config.h"
+#include "led.h"
+
+
+struct ledda_ip {
+ uint32_t _rsvd0;
+ uint32_t pwrr; /* 0001 LEDDPWRR - Pulse Width Register Red */
+ uint32_t pwrg; /* 0010 LEDDPWRG - Pulse Width Register Green */
+ uint32_t pwrb; /* 0011 LEDDPWRB - Pulse Width Register Blue */
+ uint32_t _rsvd1;
+ uint32_t bcrr; /* 0101 LEDDBCRR - Breathe Control Rise Register */
+ uint32_t bcfr; /* 0101 LEDDBCFR - Breathe Control Fall Register */
+ uint32_t _rsvd2;
+ uint32_t cr0; /* 1000 LEDDCR0 - Control Register 0 */
+ uint32_t br; /* 1001 LEDDBR - Pre-scale Register */
+ uint32_t onr; /* 1010 LEDONR - ON Time Register */
+ uint32_t ofr; /* 1011 LEDOFR - OFF Time Register */
+} __attribute__((packed,aligned(4)));
+
+#define LEDDA_IP_CR0_LEDDEN (1 << 7)
+#define LEDDA_IP_CR0_FR250 (1 << 6)
+#define LEDDA_IP_CR0_OUTPOL (1 << 5)
+#define LEDDA_IP_CR0_OUTSKEW (1 << 4)
+#define LEDDA_IP_CR0_QUICK_STOP (1 << 3)
+#define LEDDA_IP_CR0_PWM_LINEAR (0 << 2)
+#define LEDDA_IP_CR0_PWM_LFSR (1 << 2)
+#define LEDDA_IP_CR0_SCALE_MSB(x) (((x) >> 8) & 3)
+
+#define LEDDA_IP_BR_SCALE_LSB(x) ((x) & 0xff)
+
+#define LEDDA_IP_ONOFF_TIME_MS(x) (((x) >> 5) & 0xff) /* 32ms interval up to 8s */
+
+#define LEDDA_IP_BREATHE_ENABLE (1 << 7)
+#define LEDDA_IP_BREATHE_MODULATE (1 << 5)
+#define LEDDA_IP_BREATHE_TIME_MS(x) (((x) >> 7) & 0x0f) /* 128ms interval up to 2s */
+
+
+struct led {
+ uint32_t csr;
+ uint32_t _rsvd[15];
+ struct ledda_ip ip;
+} __attribute__((packed,aligned(4)));
+
+#define LED_CSR_LEDDEXE (1 << 1)
+#define LED_CSR_RGBLEDEN (1 << 2)
+#define LED_CSR_CURREN (1 << 3)
+
+
+static volatile struct led * const led_regs = (void*)(LED_BASE);
+
+static const uint32_t led_cr0_base =
+ LEDDA_IP_CR0_FR250 |
+ LEDDA_IP_CR0_OUTSKEW |
+ LEDDA_IP_CR0_QUICK_STOP |
+ LEDDA_IP_CR0_PWM_LFSR |
+ LEDDA_IP_CR0_SCALE_MSB(480);
+
+
+void
+led_init(void)
+{
+ led_regs->ip.pwrr = 0;
+ led_regs->ip.pwrg = 0;
+ led_regs->ip.pwrb = 0;
+
+ led_regs->ip.bcrr = 0;
+ led_regs->ip.bcfr = 0;
+
+ led_regs->ip.onr = 0;
+ led_regs->ip.ofr = 0;
+
+ led_regs->ip.br = LEDDA_IP_BR_SCALE_LSB(480);
+ led_regs->ip.cr0 = led_cr0_base;
+
+ led_regs->csr = LED_CSR_LEDDEXE | LED_CSR_RGBLEDEN | LED_CSR_CURREN;
+}
+
+void
+led_color(uint8_t r, uint8_t g, uint8_t b)
+{
+#if defined(BOARD_ICE1USB)
+ // icE1usb
+ led_regs->ip.pwrr = b;
+ led_regs->ip.pwrg = g;
+ led_regs->ip.pwrb = r;
+#elif defined(BOARD_ICE1USB_PROTO_ICEBREAKER)
+ // iCEBreaker v1.0b tnt
+ led_regs->ip.pwrr = r;
+ led_regs->ip.pwrg = b;
+ led_regs->ip.pwrb = g;
+/*
+ // iCEBreaker v1.0c+
+ led_regs->ip.pwrr = b;
+ led_regs->ip.pwrg = g;
+ led_regs->ip.pwrb = r;
+ */
+#elif defined(BOARD_ICE1USB_PROTO_BITSY)
+ // iCEBreaker bitsy v0 (RGB led 'hacked on')
+ led_regs->ip.pwrr = g;
+ led_regs->ip.pwrg = r;
+ led_regs->ip.pwrb = b;
+#elif defined(BOARD_E1_TRACER)
+ // E1 tracer
+ led_regs->ip.pwrr = b;
+ led_regs->ip.pwrg = g;
+ led_regs->ip.pwrb = r;
+#else
+ // Default / Unknown
+ led_regs->ip.pwrr = r;
+ led_regs->ip.pwrg = g;
+ led_regs->ip.pwrb = b;
+#endif
+}
+
+void
+led_state(bool on)
+{
+ if (on)
+ led_regs->ip.cr0 = led_cr0_base | LEDDA_IP_CR0_LEDDEN;
+ else
+ led_regs->ip.cr0 = led_cr0_base;
+}
+
+void
+led_blink(bool enabled, int on_time_ms, int off_time_ms)
+{
+ /* Disable EXE before doing any change */
+ led_regs->csr = LED_CSR_RGBLEDEN | LED_CSR_CURREN;
+
+ /* Load new config */
+ if (enabled) {
+ led_regs->ip.onr = LEDDA_IP_ONOFF_TIME_MS(on_time_ms);
+ led_regs->ip.ofr = LEDDA_IP_ONOFF_TIME_MS(off_time_ms);
+ } else {
+ led_regs->ip.onr = 0;
+ led_regs->ip.ofr = 0;
+ }
+
+ /* Re-enable execution */
+ led_regs->csr = LED_CSR_LEDDEXE | LED_CSR_RGBLEDEN | LED_CSR_CURREN;
+}
+
+void
+led_breathe(bool enabled, int rise_time_ms, int fall_time_ms)
+{
+ if (enabled) {
+ led_regs->ip.bcrr = LEDDA_IP_BREATHE_ENABLE |
+ LEDDA_IP_BREATHE_MODULATE |
+ LEDDA_IP_BREATHE_TIME_MS(rise_time_ms);
+ led_regs->ip.bcfr = LEDDA_IP_BREATHE_ENABLE |
+ LEDDA_IP_BREATHE_MODULATE |
+ LEDDA_IP_BREATHE_TIME_MS(fall_time_ms);
+ } else {
+ led_regs->ip.bcrr = 0;
+ led_regs->ip.bcfr = 0;
+ }
+}