From c308334de7f7fb92504364f29bd815014002d3ce Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Thu, 13 Jan 2022 00:50:10 +0100 Subject: [PATCH] icE1usb fw: Add USB control for the GPSDO function Signed-off-by: Sylvain Munaut Change-Id: If92aa68d8a49349fa0d6d556eec81bbb80be989c --- firmware/ice40-riscv/icE1usb/gpsdo.c | 68 +++++++++++++++++++ firmware/ice40-riscv/icE1usb/gpsdo.h | 11 +++ firmware/ice40-riscv/icE1usb/ice1usb_proto.h | 39 +++++++++++ firmware/ice40-riscv/icE1usb/usb_dev.c | 71 ++++++++++++++++++++ 4 files changed, 189 insertions(+) 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 @@ vctxo_sensitivity[] = { #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 @@ enum gpsdo_vctxo_model { 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 #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 @@ _usb_dev_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer) 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; }