diff options
Diffstat (limited to 'libloragw/src/loragw_fpga.c')
-rw-r--r-- | libloragw/src/loragw_fpga.c | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/libloragw/src/loragw_fpga.c b/libloragw/src/loragw_fpga.c new file mode 100644 index 0000000..465f43e --- /dev/null +++ b/libloragw/src/loragw_fpga.c @@ -0,0 +1,352 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Functions used to handle FPGA register access for LoRa concentrator. + Registers are addressed by name. + Multi-bytes registers are handled automatically. + Read-modify-write is handled automatically. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf */ + +#include "loragw_spi.h" +#include "loragw_aux.h" +#include "loragw_hal.h" +#include "loragw_reg.h" +#include "loragw_fpga.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_REG == 1 + #define DEBUG_MSG(str) fprintf(stderr, str) + #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) + #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;} +#else + #define DEBUG_MSG(str) + #define DEBUG_PRINTF(fmt, args...) + #define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;} +#endif + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +/* +auto generated register mapping for C code : 11-Jul-2013 13:20:40 +this file contains autogenerated C struct used to access the LoRa register from the Primer firmware +this file is autogenerated from registers description +293 registers are defined +*/ +const struct lgw_reg_s fpga_regs[LGW_FPGA_TOTALREGS] = { + {-1,0,0,0,1,0,0}, /* SOFT_RESET */ + {-1,0,1,0,4,1,0}, /* FPGA_FEATURE */ + {-1,0,5,0,3,1,0}, /* LBT_INITIAL_FREQ */ + {-1,1,0,0,8,1,0}, /* VERSION */ + {-1,2,0,0,8,1,0}, /* FPGA_STATUS */ + {-1,3,0,0,1,0,0}, /* FPGA_CTRL_FEATURE_START */ + {-1,3,1,0,1,0,0}, /* FPGA_CTRL_RADIO_RESET */ + {-1,3,2,0,1,0,0}, /* FPGA_CTRL_INPUT_SYNC_I */ + {-1,3,3,0,1,0,0}, /* FPGA_CTRL_INPUT_SYNC_Q */ + {-1,3,4,0,1,0,0}, /* FPGA_CTRL_OUTPUT_SYNC */ + {-1,3,5,0,1,0,0}, /* FPGA_CTRL_INVERT_IQ */ + {-1,3,6,0,1,0,0}, /* FPGA_CTRL_ACCESS_HISTO_MEM */ + {-1,3,7,0,1,0,0}, /* FPGA_CTRL_CLEAR_HISTO_MEM */ + {-1,4,0,0,8,0,0}, /* HISTO_RAM_ADDR */ + {-1,5,0,0,8,1,0}, /* HISTO_RAM_DATA */ + {-1,8,0,0,16,0,1000}, /* HISTO_NB_READ */ + {-1,14,0,0,16,1,0}, /* LBT_TIMESTAMP_CH */ + {-1,17,0,0,4,0,0}, /* LBT_TIMESTAMP_SELECT_CH */ + {-1,18,0,0,8,0,0}, /* LBT_CH0_FREQ_OFFSET */ + {-1,19,0,0,8,0,0}, /* LBT_CH1_FREQ_OFFSET */ + {-1,20,0,0,8,0,0}, /* LBT_CH2_FREQ_OFFSET */ + {-1,21,0,0,8,0,0}, /* LBT_CH3_FREQ_OFFSET */ + {-1,22,0,0,8,0,0}, /* LBT_CH4_FREQ_OFFSET */ + {-1,23,0,0,8,0,0}, /* LBT_CH5_FREQ_OFFSET */ + {-1,24,0,0,8,0,0}, /* LBT_CH6_FREQ_OFFSET */ + {-1,25,0,0,8,0,0}, /* LBT_CH7_FREQ_OFFSET */ + {-1,26,0,0,8,0,0}, /* SCAN_FREQ_OFFSET */ + {-1,28,0,0,1,0,0}, /* LBT_SCAN_TIME_CH0 */ + {-1,28,1,0,1,0,0}, /* LBT_SCAN_TIME_CH1 */ + {-1,28,2,0,1,0,0}, /* LBT_SCAN_TIME_CH2 */ + {-1,28,3,0,1,0,0}, /* LBT_SCAN_TIME_CH3 */ + {-1,28,4,0,1,0,0}, /* LBT_SCAN_TIME_CH4 */ + {-1,28,5,0,1,0,0}, /* LBT_SCAN_TIME_CH5 */ + {-1,28,6,0,1,0,0}, /* LBT_SCAN_TIME_CH6 */ + {-1,28,7,0,1,0,0}, /* LBT_SCAN_TIME_CH7 */ + {-1,30,0,0,8,0,160}, /* RSSI_TARGET */ + {-1,31,0,0,24,0,0}, /* HISTO_SCAN_FREQ */ + {-1,34,0,0,6,0,0} /* NOTCH_FREQ_OFFSET */ +}; + +/* -------------------------------------------------------------------------- */ +/* --- INTERNAL SHARED VARIABLES -------------------------------------------- */ + +extern void *lgw_spi_target; /*! generic pointer to the SPI device */ +extern uint8_t lgw_spi_mux_mode; /*! current SPI mux mode used */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ +static bool tx_notch_support = false; +static uint8_t tx_notch_offset; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +float lgw_fpga_get_tx_notch_delay(void) { + float tx_notch_delay; + + if (tx_notch_support == false) { + return 0; + } + + /* Notch filtering performed by FPGA adds a constant delay (group delay) that we need to compensate */ + tx_notch_delay = (31.25 * ((64 + tx_notch_offset) / 2)) / 1E3; /* 32MHz => 31.25ns */ + + return tx_notch_delay; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_fpga_configure(uint32_t tx_notch_freq) { + int x; + int32_t val; + bool spectral_scan_support, lbt_support; + + /* Check input parameters */ + if ((tx_notch_freq < LGW_MIN_NOTCH_FREQ) || (tx_notch_freq > LGW_MAX_NOTCH_FREQ)) { + DEBUG_PRINTF("WARNING: FPGA TX notch frequency is out of range (%u - [%u..%u]), setting it to default (%u)\n", tx_notch_freq, LGW_MIN_NOTCH_FREQ, LGW_MAX_NOTCH_FREQ, LGW_DEFAULT_NOTCH_FREQ); + tx_notch_freq = LGW_DEFAULT_NOTCH_FREQ; + } + + /* Get supported FPGA features */ + printf("INFO: FPGA supported features:"); + lgw_fpga_reg_r(LGW_FPGA_FEATURE, &val); + tx_notch_support = TAKE_N_BITS_FROM((uint8_t)val, 0, 1); + if (tx_notch_support == true) { + printf(" [TX filter] "); + } + spectral_scan_support = TAKE_N_BITS_FROM((uint8_t)val, 1, 1); + if (spectral_scan_support == true) { + printf(" [Spectral Scan] "); + } + lbt_support = TAKE_N_BITS_FROM((uint8_t)val, 2, 1); + if (lbt_support == true) { + printf(" [LBT] "); + } + printf("\n"); + + x = lgw_fpga_reg_w(LGW_FPGA_CTRL_INPUT_SYNC_I, 1); + x |= lgw_fpga_reg_w(LGW_FPGA_CTRL_INPUT_SYNC_Q, 1); + x |= lgw_fpga_reg_w(LGW_FPGA_CTRL_OUTPUT_SYNC, 0); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure FPGA TX synchro\n"); + return LGW_REG_ERROR; + } + + /* Required for Semtech AP2 reference design */ + x = lgw_fpga_reg_w(LGW_FPGA_CTRL_INVERT_IQ, 1); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure FPGA polarity\n"); + return LGW_REG_ERROR; + } + + /* Configure TX notch filter */ + if (tx_notch_support == true) { + tx_notch_offset = (32E6 / (2*tx_notch_freq)) - 64; + x = lgw_fpga_reg_w(LGW_FPGA_NOTCH_FREQ_OFFSET, (int32_t)tx_notch_offset); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure FPGA TX notch filter\n"); + return LGW_REG_ERROR; + } + + /* Readback to check that notch frequency is programmable */ + x = lgw_fpga_reg_r(LGW_FPGA_NOTCH_FREQ_OFFSET, &val); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to read FPGA TX notch frequency\n"); + return LGW_REG_ERROR; + } + if (val != tx_notch_offset) { + DEBUG_MSG("WARNING: TX notch filter frequency is not programmable (check your FPGA image)\n"); + } else { + DEBUG_PRINTF("INFO: TX notch filter frequency set to %u (%i)\n", tx_notch_freq, tx_notch_offset); + } + } + + return LGW_REG_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Write to a register addressed by name */ +int lgw_fpga_reg_w(uint16_t register_id, int32_t reg_value) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + if (register_id >= LGW_FPGA_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if (lgw_spi_target == NULL) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = fpga_regs[register_id]; + + /* reject write to read-only registers */ + if (r.rdon == 1){ + DEBUG_MSG("ERROR: TRYING TO WRITE A READ-ONLY REGISTER\n"); + return LGW_REG_ERROR; + } + + spi_stat += reg_w_align32(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r, reg_value); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER WRITE\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Read to a register addressed by name */ +int lgw_fpga_reg_r(uint16_t register_id, int32_t *reg_value) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + CHECK_NULL(reg_value); + if (register_id >= LGW_FPGA_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if (lgw_spi_target == NULL) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = fpga_regs[register_id]; + + spi_stat += reg_r_align32(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r, reg_value); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER WRITE\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Point to a register by name and do a burst write */ +int lgw_fpga_reg_wb(uint16_t register_id, uint8_t *data, uint16_t size) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + CHECK_NULL(data); + if (size == 0) { + DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); + return LGW_REG_ERROR; + } + if (register_id >= LGW_FPGA_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if (lgw_spi_target == NULL) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = fpga_regs[register_id]; + + /* reject write to read-only registers */ + if (r.rdon == 1){ + DEBUG_MSG("ERROR: TRYING TO BURST WRITE A READ-ONLY REGISTER\n"); + return LGW_REG_ERROR; + } + + /* do the burst write */ + spi_stat += lgw_spi_wb(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r.addr, data, size); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER BURST WRITE\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Point to a register by name and do a burst read */ +int lgw_fpga_reg_rb(uint16_t register_id, uint8_t *data, uint16_t size) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + CHECK_NULL(data); + if (size == 0) { + DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); + return LGW_REG_ERROR; + } + if (register_id >= LGW_FPGA_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if (lgw_spi_target == NULL) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = fpga_regs[register_id]; + + /* do the burst read */ + spi_stat += lgw_spi_rb(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r.addr, data, size); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER BURST READ\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* --- EOF ------------------------------------------------------------------ */ |