icE1usb fw: Add USB control for the GPSDO function
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Change-Id: If92aa68d8a49349fa0d6d556eec81bbb80be989c
diff --git a/firmware/ice40-riscv/icE1usb/gpsdo.c b/firmware/ice40-riscv/icE1usb/gpsdo.c
index ca9b806..ae131d4 100644
--- a/firmware/ice40-riscv/icE1usb/gpsdo.c
+++ b/firmware/ice40-riscv/icE1usb/gpsdo.c
@@ -13,6 +13,8 @@
#include "gpsdo.h"
#include "misc.h"
+#include "ice1usb_proto.h"
+
struct {
/* Current tuning */
@@ -74,6 +76,72 @@
#define MAX_INVALID 5
+static void _gpsdo_coarse_start(void);
+
+
+void
+gpsdo_get_status(struct e1usb_gpsdo_status *status)
+{
+ const uint8_t state_map[] = {
+ [STATE_DISABLED] = ICE1USB_GPSDO_STATE_DISABLED,
+ [STATE_HOLD_OVER] = ICE1USB_GPSDO_STATE_HOLD_OVER,
+ [STATE_TUNE_COARSE] = ICE1USB_GPSDO_STATE_TUNE_COARSE,
+ [STATE_TUNE_FINE] = ICE1USB_GPSDO_STATE_TUNE_FINE,
+ };
+ const uint8_t ant_map[] = {
+ [ANT_UNKNOWN] = ICE1USB_GPSDO_ANT_UNKNOWN,
+ [ANT_OK] = ICE1USB_GPSDO_ANT_OK,
+ [ANT_OPEN] = ICE1USB_GPSDO_ANT_OPEN,
+ [ANT_SHORT] = ICE1USB_GPSDO_ANT_SHORT,
+ };
+
+ status->state = state_map[g_gpsdo.state];
+ status->antenna_status = ant_map[gps_antenna_status()];
+ status->valid_fix = gps_has_valid_fix();
+ status->mode = (g_gpsdo.state == STATE_DISABLED) ? ICE1USB_GPSDO_MODE_DISABLED : ICE1USB_GPSDO_MODE_AUTO;
+ status->tune.coarse = g_gpsdo.tune.coarse;
+ status->tune.fine = g_gpsdo.tune.fine;
+ status->freq_est = g_gpsdo.meas.last;
+}
+
+void
+gpsdo_enable(bool enable)
+{
+ if (!enable)
+ g_gpsdo.state = STATE_DISABLED;
+ else if (g_gpsdo.state == STATE_DISABLED)
+ g_gpsdo.state = STATE_HOLD_OVER;
+}
+
+bool
+gpsdo_enabled(void)
+{
+ return g_gpsdo.state != STATE_DISABLED;
+}
+
+void
+gpsdo_set_tune(uint16_t coarse, uint16_t fine)
+{
+ /* Set value */
+ g_gpsdo.tune.coarse = coarse;
+ g_gpsdo.tune.fine = fine;
+
+ pdm_set(PDM_CLK_HI, true, g_gpsdo.tune.coarse, false);
+ pdm_set(PDM_CLK_LO, true, g_gpsdo.tune.fine, false);
+
+ /* If we were in 'fine' mode, reset to coarse */
+ if (g_gpsdo.state == STATE_TUNE_FINE)
+ _gpsdo_coarse_start();
+}
+
+void
+gpsdo_get_tune(uint16_t *coarse, uint16_t *fine)
+{
+ *coarse = g_gpsdo.tune.coarse;
+ *fine = g_gpsdo.tune.fine;
+}
+
+
static void
_gpsdo_coarse_start(void)
{
diff --git a/firmware/ice40-riscv/icE1usb/gpsdo.h b/firmware/ice40-riscv/icE1usb/gpsdo.h
index 181e4b9..a771db7 100644
--- a/firmware/ice40-riscv/icE1usb/gpsdo.h
+++ b/firmware/ice40-riscv/icE1usb/gpsdo.h
@@ -12,5 +12,16 @@
VCTXO_SITIME_SIT3808_E = 1, /* SIT3808AI-D2-33EE-30.720000T */
};
+struct e1usb_gpsdo_status;
+
+
+void gpsdo_get_status(struct e1usb_gpsdo_status *status);
+
+void gpsdo_enable(bool enable);
+bool gpsdo_enabled(void);
+
+void gpsdo_set_tune(uint16_t coarse, uint16_t fine);
+void gpsdo_get_tune(uint16_t *coarse, uint16_t *fine);
+
void gpsdo_poll(void);
void gpsdo_init(enum gpsdo_vctxo_model vctxo);
diff --git a/firmware/ice40-riscv/icE1usb/ice1usb_proto.h b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
index 9ac2136..a249537 100644
--- a/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
+++ b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
@@ -17,12 +17,51 @@
/*! returns a bit-mask of optional device capabilities (see enum e1usb_dev_capability) */
#define ICE1USB_DEV_GET_CAPABILITIES 0x01
#define ICE1USB_DEV_GET_FW_BUILD 0x02
+#define ICE1USB_DEV_GET_GPSDO_STATUS 0x10
+#define ICE1USB_DEV_GET_GPSDO_MODE 0x12 /*!< uint8_t */
+#define ICE1USB_DEV_SET_GPSDO_MODE 0x13 /*!< wValue = mode */
+#define ICE1USB_DEV_GET_GPSDO_TUNE 0x14 /*!< data = struct e1usb_gpsdo_tune */
+#define ICE1USB_DEV_SET_GPSDO_TUNE 0x15 /*!< data = struct e1usb_gpsdo_tune */
enum e1usb_dev_capability {
/*! Does this board have a GPS-DO */
ICE1USB_DEV_CAP_GPSDO,
};
+enum ice1usb_gpsdo_mode {
+ ICE1USB_GPSDO_MODE_DISABLED = 0,
+ ICE1USB_GPSDO_MODE_AUTO = 1,
+};
+
+enum ice1usb_gpsdo_antenna_state {
+ ICE1USB_GPSDO_ANT_UNKNOWN = 0,
+ ICE1USB_GPSDO_ANT_OK = 1,
+ ICE1USB_GPSDO_ANT_OPEN = 2,
+ ICE1USB_GPSDO_ANT_SHORT = 3,
+};
+
+enum ice1usb_gpsdo_state {
+ ICE1USB_GPSDO_STATE_DISABLED = 0,
+ ICE1USB_GPSDO_STATE_CALIBRATE = 1,
+ ICE1USB_GPSDO_STATE_HOLD_OVER = 2,
+ ICE1USB_GPSDO_STATE_TUNE_COARSE = 3,
+ ICE1USB_GPSDO_STATE_TUNE_FINE = 4,
+};
+
+struct e1usb_gpsdo_tune {
+ uint16_t coarse;
+ uint16_t fine;
+} __attribute__((packed));
+
+struct e1usb_gpsdo_status {
+ uint8_t state;
+ uint8_t antenna_status; /*!< Antenna status */
+ uint8_t valid_fix; /*!< Valid GPS Fix (0/1) */
+ uint8_t mode; /*!< Current configured operating mode */
+ struct e1usb_gpsdo_tune tune; /*!< Current VCXO tuning values */
+ uint32_t freq_est; /*!< Latest frequency estimate measurement */
+} __attribute__((packed));
+
/* Interface Requests */
diff --git a/firmware/ice40-riscv/icE1usb/usb_dev.c b/firmware/ice40-riscv/icE1usb/usb_dev.c
index e19d9a0..2997a33 100644
--- a/firmware/ice40-riscv/icE1usb/usb_dev.c
+++ b/firmware/ice40-riscv/icE1usb/usb_dev.c
@@ -12,6 +12,7 @@
#include <no2usb/usb_proto.h>
#include "console.h"
+#include "gpsdo.h"
#include "misc.h"
#include "ice1usb_proto.h"
@@ -20,6 +21,61 @@
const char *fw_build_str = BUILD_INFO;
+static void
+_get_gpsdo_status(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ struct e1usb_gpsdo_status status;
+
+ gpsdo_get_status(&status);
+
+ memcpy(xfer->data, &status, sizeof(struct e1usb_gpsdo_status));
+ xfer->len = sizeof(struct e1usb_gpsdo_status);
+}
+
+static void
+_get_gpsdo_mode(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ xfer->data[0] = gpsdo_enabled() ? ICE1USB_GPSDO_MODE_DISABLED : ICE1USB_GPSDO_MODE_AUTO;
+ xfer->len = 1;
+}
+
+static void
+_set_gpsdo_mode(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ gpsdo_enable(req->wValue != ICE1USB_GPSDO_MODE_DISABLED);
+}
+
+static void
+_get_gpsdo_tune(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ uint16_t coarse, fine;
+ struct e1usb_gpsdo_tune tune;
+
+ gpsdo_get_tune(&coarse, &fine);
+ tune.coarse = coarse;
+ tune.fine = fine;
+
+ memcpy(xfer->data, &tune, sizeof(struct e1usb_gpsdo_tune));
+ xfer->len = sizeof(struct e1usb_gpsdo_tune);
+}
+
+static bool
+_set_gpsdo_tune_done(struct usb_xfer *xfer)
+{
+ const struct e1usb_gpsdo_tune *tune = (const void *) xfer->data;
+ gpsdo_set_tune(tune->coarse, tune->fine);
+ return true;
+}
+
+static void
+_set_gpsdo_tune(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ xfer->cb_done = _set_gpsdo_tune_done;
+ xfer->cb_ctx = req;
+ xfer->len = sizeof(struct e1usb_gpsdo_tune);
+}
+
+
static enum usb_fnd_resp
_usb_dev_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer)
{
@@ -37,6 +93,21 @@
xfer->data = (void*) fw_build_str;
xfer->len = strlen(fw_build_str);
break;
+ case ICE1USB_DEV_GET_GPSDO_STATUS:
+ _get_gpsdo_status(req, xfer);
+ break;
+ case ICE1USB_DEV_GET_GPSDO_MODE:
+ _get_gpsdo_mode(req, xfer);
+ break;
+ case ICE1USB_DEV_SET_GPSDO_MODE:
+ _set_gpsdo_mode(req, xfer);
+ break;
+ case ICE1USB_DEV_GET_GPSDO_TUNE:
+ _get_gpsdo_tune(req, xfer);
+ break;
+ case ICE1USB_DEV_SET_GPSDO_TUNE:
+ _set_gpsdo_tune(req, xfer);
+ break;
default:
return USB_FND_ERROR;
}