diff options
author | Arturs Artamonovs <arturs.artamonovs@protonmail.com> | 2024-11-03 15:56:55 +0000 |
---|---|---|
committer | Arturs Artamonovs <arturs.artamonovs@protonmail.com> | 2024-11-03 15:56:55 +0000 |
commit | cf4444e7390365df43ecbd3d130015c1e06ef88f (patch) | |
tree | 8a6eb114135a04d5efd5af213577b4fac47532ae /Radio/HW/BladeRF/src/driver | |
parent | ca50c0f64f1b2fce46b4cb83ed111854bac13852 (diff) | |
download | PrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.tar.gz PrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.zip |
BladeRF library compiles
Diffstat (limited to 'Radio/HW/BladeRF/src/driver')
-rw-r--r-- | Radio/HW/BladeRF/src/driver/dac161s055.c | 65 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/dac161s055.h | 51 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/fpga_trigger.c | 196 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/fpga_trigger.h | 128 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/fx3_fw.c | 343 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/fx3_fw.h | 79 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/ina219.c | 154 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/ina219.h | 83 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/si5338.c | 669 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/si5338.h | 134 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/smb_clock.c | 211 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/smb_clock.h | 47 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/spi_flash.c | 134 | ||||
-rw-r--r-- | Radio/HW/BladeRF/src/driver/spi_flash.h | 99 |
14 files changed, 2393 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/driver/dac161s055.c b/Radio/HW/BladeRF/src/driver/dac161s055.c new file mode 100644 index 0000000..241b4c3 --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/dac161s055.c @@ -0,0 +1,65 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2013 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <libbladeRF.h> + +#include "rel_assert.h" +#include "host_config.h" +#include "log.h" + +#include "dac161s055.h" + +int dac161s055_write(struct bladerf *dev, uint16_t value) +{ + int status; + + /* Ensure device is in write-through mode */ + status = dev->backend->vctcxo_dac_write(dev, 0x28, 0x0000); + if (status < 0) { + return status; + } + + /* Write DAC value to channel 0 */ + status = dev->backend->vctcxo_dac_write(dev, 0x08, value); + if (status < 0) { + return status; + } + + log_verbose("%s: Wrote 0x%04x\n", __FUNCTION__, value); + + return 0; +} + +int dac161s055_read(struct bladerf *dev, uint16_t *value) +{ + int status; + + /* Read DAC value for channel 0 */ + status = dev->backend->vctcxo_dac_read(dev, 0x98, value); + if (status < 0) { + *value = 0; + return status; + } + + log_verbose("%s: Read 0x%04x\n", __FUNCTION__, *value); + + return 0; +} diff --git a/Radio/HW/BladeRF/src/driver/dac161s055.h b/Radio/HW/BladeRF/src/driver/dac161s055.h new file mode 100644 index 0000000..009827b --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/dac161s055.h @@ -0,0 +1,51 @@ +/** + * @file dac161s055.h + * + * @brief DAC161S055 Support + * + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2017777777 LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DRIVER_DAC161S055_H_ +#define DRIVER_DAC161S055_H_ + +#include "board/board.h" + +/** + * Write the output value to the DAC. + * + * @param dev Device handle + * @param[in] value Value + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int dac161s055_write(struct bladerf *dev, uint16_t value); + +/** + * Read the output value of the DAC. + * + * @param dev Device handle + * @param[out] value Value + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int dac161s055_read(struct bladerf *dev, uint16_t *value); + +#endif diff --git a/Radio/HW/BladeRF/src/driver/fpga_trigger.c b/Radio/HW/BladeRF/src/driver/fpga_trigger.c new file mode 100644 index 0000000..67818a0 --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/fpga_trigger.c @@ -0,0 +1,196 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2016 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <libbladeRF.h> + +#include "log.h" + +#include "fpga_trigger.h" + +static bool is_valid_signal(bladerf_trigger_signal signal) +{ + switch (signal) { + case BLADERF_TRIGGER_J71_4: + case BLADERF_TRIGGER_J51_1: + case BLADERF_TRIGGER_MINI_EXP_1: + + case BLADERF_TRIGGER_USER_0: + case BLADERF_TRIGGER_USER_1: + case BLADERF_TRIGGER_USER_2: + case BLADERF_TRIGGER_USER_3: + case BLADERF_TRIGGER_USER_4: + case BLADERF_TRIGGER_USER_5: + case BLADERF_TRIGGER_USER_6: + case BLADERF_TRIGGER_USER_7: + return true; + + default: + log_debug("Invalid trigger signal: %d\n", signal); + return false; + } +} + +int fpga_trigger_read(struct bladerf *dev, bladerf_channel ch, + bladerf_trigger_signal signal, uint8_t *regval) +{ + if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0)) + return BLADERF_ERR_INVAL; + + if (!is_valid_signal(signal)) + return BLADERF_ERR_INVAL; + + return dev->backend->read_trigger(dev, ch, signal, regval); +} + +int fpga_trigger_write(struct bladerf *dev, bladerf_channel ch, + bladerf_trigger_signal signal, uint8_t regval) +{ + if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0)) + return BLADERF_ERR_INVAL; + + if (!is_valid_signal(signal)) + return BLADERF_ERR_INVAL; + + return dev->backend->write_trigger(dev, ch, signal, regval); +} + +int fpga_trigger_init(struct bladerf *dev, bladerf_channel ch, + bladerf_trigger_signal signal, + struct bladerf_trigger *trigger) + +{ + int status; + uint8_t regval; + + trigger->options = 0; + + status = fpga_trigger_read(dev, ch, signal, ®val); + if (status != 0) { + trigger->channel = BLADERF_CHANNEL_INVALID; + trigger->role = BLADERF_TRIGGER_ROLE_INVALID; + trigger->signal = BLADERF_TRIGGER_INVALID; + return status; + } + + if ((regval & BLADERF_TRIGGER_REG_MASTER) != 0) { + trigger->role = BLADERF_TRIGGER_ROLE_MASTER; + } else { + trigger->role = BLADERF_TRIGGER_ROLE_SLAVE; + } + + trigger->channel = ch; + trigger->signal = signal; + + return 0; +} + +int fpga_trigger_arm(struct bladerf *dev, + const struct bladerf_trigger *trigger, bool arm) +{ + int status; + uint8_t regval; + + status = fpga_trigger_read(dev, trigger->channel, trigger->signal, ®val); + if (status != 0) { + return status; + } + + /* Reset any previous fire request */ + regval &= ~BLADERF_TRIGGER_REG_FIRE; + + if (arm) { + regval |= BLADERF_TRIGGER_REG_ARM; + } else { + regval &= ~BLADERF_TRIGGER_REG_ARM; + } + + switch (trigger->role) { + case BLADERF_TRIGGER_ROLE_MASTER: + regval |= BLADERF_TRIGGER_REG_MASTER; + break; + + case BLADERF_TRIGGER_ROLE_SLAVE: + regval &= ~BLADERF_TRIGGER_REG_MASTER; + break; + + case BLADERF_TRIGGER_ROLE_DISABLED: + regval = 0; + break; + + default: + log_debug("Invalid trigger role: %d\n", trigger->role); + return BLADERF_ERR_INVAL; + } + + status = fpga_trigger_write(dev, trigger->channel, trigger->signal, regval); + + return status; +} + +int fpga_trigger_fire(struct bladerf *dev, + const struct bladerf_trigger *trigger) +{ + int status; + uint8_t regval; + + status = fpga_trigger_read(dev, trigger->channel, trigger->signal, ®val); + if (status != 0) { + return status; + } + + regval |= BLADERF_TRIGGER_REG_FIRE; + status = fpga_trigger_write(dev, trigger->channel, trigger->signal, regval); + + return status; +} + +int fpga_trigger_state(struct bladerf *dev, const struct bladerf_trigger *trigger, + bool *is_armed, bool *fired, bool *fire_requested) +{ + int status; + uint8_t regval; + + status = fpga_trigger_read(dev, trigger->channel, trigger->signal, ®val); + if (status != 0) { + *fired = false; + return status; + } + + if (is_armed != NULL) { + *is_armed = (regval & BLADERF_TRIGGER_REG_ARM) != 0; + } + + if (fired != NULL) { + /* Signal is active-low */ + *fired = (regval & BLADERF_TRIGGER_REG_LINE) == 0; + } + + if (fire_requested != NULL) { + if (trigger->role == BLADERF_TRIGGER_ROLE_MASTER) { + *fire_requested = (regval & BLADERF_TRIGGER_REG_FIRE) != 0; + } else { + *fire_requested = false; + } + } + + return status; +} + diff --git a/Radio/HW/BladeRF/src/driver/fpga_trigger.h b/Radio/HW/BladeRF/src/driver/fpga_trigger.h new file mode 100644 index 0000000..8995fed --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/fpga_trigger.h @@ -0,0 +1,128 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2016 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DRIVER_FPGA_TRIGGER_H_ +#define DRIVER_FPGA_TRIGGER_H_ + +#include "board/board.h" + +/** + * Read trigger control register + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] signal Trigger signal control register to read from + * @param[out] val Pointer to variable that register is read into See the + * BLADERF_TRIGGER_REG_* macros for the meaning of each + * bit. + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int fpga_trigger_read(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t *val); + +/** + * Write trigger control register + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] signal Trigger signal to configure + * @param[in] val Data to write into the trigger control register. See + * the BLADERF_TRIGGER_REG_* macros for options. + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int fpga_trigger_write(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t val); + + +/** + * Initialize a bladerf_trigger structure based upon the current state + * of a channel's trigger control register. + * + * @param dev Device to query + * @param[in] ch Channel + * @param[in] signal Trigger signal to query + * @param[out] trigger Updated to describe trigger + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int fpga_trigger_init(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal signal, + struct bladerf_trigger *trigger); + +/** + * Arm or re-arm the specified trigger. + * + * @param dev Device handle + * @param[in] trigger Description of trigger to arm + * @param[in] arm If true, the specified trigger will be armed. Setting + * this to false will disarm the trigger specified in + * `config`. + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int fpga_trigger_arm(struct bladerf *dev, + const struct bladerf_trigger *trigger, + bool arm); + +/** + * Fire a trigger event. + * + * Calling this functiona with a trigger whose role is anything other than + * ::BLADERF_TRIGGER_REG_MASTER will yield a BLADERF_ERR_INVAL return value. + * + * @param dev Device handle + * @param[in] trigger Trigger to assert + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int fpga_trigger_fire(struct bladerf *dev, + const struct bladerf_trigger *trigger); + +/** + * Query the fire request status of a master trigger + * + * @param dev Device handle + * @param[in] trigger Trigger to query + * @param[out] is_armed Set to true if the trigger is armed, and false + * otherwise. May be NULL. + * @param[out] has_fired Set to true if the trigger has fired, and false + * otherwise. May be NULL. + * @param[out] fire_requested Only applicable to a trigger master. Set to + * true if a fire request has been previously + * submitted. May be NULL. + * @param[out] resv1 Reserved parameter. Set to NULL. + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int fpga_trigger_state(struct bladerf *dev, + const struct bladerf_trigger *trigger, + bool *is_armed, + bool *has_fired, + bool *fire_requested); + +#endif diff --git a/Radio/HW/BladeRF/src/driver/fx3_fw.c b/Radio/HW/BladeRF/src/driver/fx3_fw.c new file mode 100644 index 0000000..423322b --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/fx3_fw.c @@ -0,0 +1,343 @@ +/* + * This file implements functionality for reading and validating an FX3 firmware + * image, and providing access to the image contents. + * + * Details about the image format can be found and FX3 bootloader can be found + * in Cypress AN76405: EZ-USB (R) FX3 (TM) Boot Options: + * http://www.cypress.com/?docID=49862 + * + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2014 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <stdint.h> + +#include <libbladeRF.h> + +#include "rel_assert.h" +#include "host_config.h" +#include "log.h" + +#include "fx3_fw.h" + +#define FX3_IMAGE_TYPE_NORMAL 0xb0 /* "Normal" image with checksum */ + +#define FX3_HDR_SIG_IDX 0x00 +#define FX3_HDR_IMAGE_CTL_IDX 0x02 +#define FX3_HDR_IMAGE_TYPE_IDX 0x03 +#define FX3_HDR_IMAGE_LEN0_IDX 0x04 +#define FX3_HDR_IMAGE_ADDR0_IDX 0x08 +#define FX3_HDR_IMAGE_DATA0_IDX 0x0c + +#define FX3_HDR_LEN FX3_HDR_IMAGE_DATA0_IDX + +#define FX3_RAM_SIZE_WORDS (256 * 1024 / sizeof(uint32_t)) + +struct fx3_firmware { + uint8_t *data; + uint32_t data_len; + + uint32_t entry_addr; + + uint32_t num_sections; + uint32_t curr_section; + uint32_t section_offset; +}; + +static inline uint32_t to_uint32(struct fx3_firmware *fw, uint32_t offset) +{ + uint32_t ret; + + assert((offset + sizeof(uint32_t)) <= fw->data_len); + + memcpy(&ret, &fw->data[offset], sizeof(ret)); + + return LE32_TO_HOST(ret); +} + +static inline bool is_valid_fx3_ram_addr(uint32_t addr, uint32_t len) { + bool valid = true; + + /* If you're doing something fun, wild, and crazy with the FX3 and your + * modifications of linker scripts has changed the firmware entry point, + * you'll need to add this compile-time definition to suppress this check. + * + * One potential improvement here would be to define the I-TCM and SYSMEM + * addresses at configuration/compilation-time to ensure they match + * what's in the FX3's linker script. The default values are assumed here. + */ +# ifndef BLADERF_SUPPRESS_FX3_FW_ENTRY_POINT_CHECK + const uint32_t itcm_base = 0x00000000; + const uint32_t itcm_len = 0x4000; + const uint32_t itcm_end = itcm_base + itcm_len; + + const uint32_t sysmem_base = 0x40000000; + const uint32_t sysmem_len = 0x80000; + const uint32_t sysmem_end = sysmem_base + sysmem_len; + + const bool in_itcm = (addr < itcm_end) && + (len <= itcm_len) && + ((addr + len) < itcm_end); + + const bool in_sysmem = (addr >= sysmem_base) && + (addr < sysmem_end) && + (len <= sysmem_len) && + ((addr + len) < sysmem_end); + + /* In lieu of compilers issuing warnings over the fact that the condition + * (addr >= itcm_base) is always true, this condition has been removed. + * + * Instead, an assertion has been added to catch the attention of anyone + * making a change to the above itcm_base definition, albeit a *very* + * unlikely change to make. */ + assert(itcm_base == 0); /* (addr >= itcm_base) guaranteed */ + + valid = in_itcm || in_sysmem; +# endif + + return valid; +} + +static int scan_fw_sections(struct fx3_firmware *fw) +{ + int status = 0; + bool done = false; /* Have we read all the sections? */ + uint32_t checksum = 0; + + uint32_t offset, i; /* In bytes */ + uint32_t next_section; /* Section offset in bytes */ + uint32_t section_len_words; /* FW uses units of 32-bit words */ + uint32_t section_len_bytes; /* Section length converted to bytes */ + + /* Byte offset where the checksum is expected to be */ + const uint32_t checksum_off = fw->data_len - sizeof(uint32_t); + + /* These assumptions should have been verified earlier */ + assert(checksum_off > FX3_HDR_IMAGE_DATA0_IDX); + assert((checksum_off % 4) == 0); + + offset = FX3_HDR_IMAGE_LEN0_IDX; + + while (!done) { + + /* Fetch the length of the current section */ + section_len_words = to_uint32(fw, offset); + + if (section_len_words > FX3_RAM_SIZE_WORDS) { + log_debug("Firmware section %u is unexpectedly large.\n", + fw->num_sections); + status = BLADERF_ERR_INVAL; + goto error; + } else { + section_len_bytes = (uint32_t)(section_len_words * sizeof(uint32_t)); + offset += sizeof(uint32_t); + } + + /* The list of sections is terminated by a 0 section length field */ + if (section_len_bytes == 0) { + fw->entry_addr = to_uint32(fw, offset); + if (!is_valid_fx3_ram_addr(fw->entry_addr, 0)) { + status = BLADERF_ERR_INVAL; + goto error; + } + + offset += sizeof(uint32_t); + done = true; + } else { +# if LOGGING_ENABLED + /* Just a value to print in verbose output */ + uint32_t section_start_offset = offset - sizeof(uint32_t); +# endif + + uint32_t addr = to_uint32(fw, offset); + if (!is_valid_fx3_ram_addr(addr, section_len_bytes)) { + status = BLADERF_ERR_INVAL; + goto error; + } + + offset += sizeof(uint32_t); + if (offset >= checksum_off) { + log_debug("Firmware truncated after section address.\n"); + status = BLADERF_ERR_INVAL; + goto error; + } + + next_section = offset + section_len_bytes; + + if (next_section >= checksum_off) { + log_debug("Firmware truncated in section %u\n", + fw->num_sections); + status = BLADERF_ERR_INVAL; + goto error; + } + + for (i = offset; i < next_section; i += sizeof(uint32_t)) { + checksum += to_uint32(fw, i); + } + + offset = next_section; + log_verbose("Scanned section %u at offset 0x%08x: " + "addr=0x%08x, len=0x%08x\n", + fw->num_sections, section_start_offset, + addr, section_len_words); + + fw->num_sections++; + } + } + + if (offset != checksum_off) { + log_debug("Invalid offset or junk at the end of the firmware image.\n"); + status = BLADERF_ERR_INVAL; + } else { + const uint32_t expected_checksum = to_uint32(fw, checksum_off); + + if (checksum != expected_checksum) { + log_debug("Bad checksum. Expected 0x%08x, got 0x%08x\n", + expected_checksum, checksum); + + status = BLADERF_ERR_INVAL; + } else { + log_verbose("Firmware checksum OK.\n"); + fw->section_offset = FX3_HDR_IMAGE_LEN0_IDX; + } + } + +error: + return status; +} + +int fx3_fw_parse(struct fx3_firmware **fw, uint8_t *buf, size_t buf_len) +{ + int status; + + if (buf_len > UINT32_MAX) { + /* This is just intended to catch a crazy edge case, since we're casting + * to 32-bits below. If this passes, the data length might still be well + * over the 512 KiB RAM limit. */ + log_debug("Size of provided image is too large.\n"); + return BLADERF_ERR_INVAL; + } + + if (buf_len < FX3_HDR_LEN) { + log_debug("Provided image is too short."); + return BLADERF_ERR_INVAL; + } + + if ((buf_len % 4) != 0) { + log_debug("Size of provided image is not a multiple of 4 bytes.\n"); + return BLADERF_ERR_INVAL; + } + + if (buf[FX3_HDR_SIG_IDX] != 'C' && buf[FX3_HDR_SIG_IDX + 1] != 'Y') { + log_debug("FX3 firmware does have 'CY' marker.\n"); + return BLADERF_ERR_INVAL; + } + + if (buf[3] != FX3_IMAGE_TYPE_NORMAL) { + log_debug("FX3 firmware header contained unexpected image type: " + "0x%02x\n", buf[FX3_HDR_IMAGE_TYPE_IDX]); + return BLADERF_ERR_INVAL; + } + + *fw = calloc(1, sizeof(struct fx3_firmware)); + if (*fw == NULL) { + return BLADERF_ERR_MEM; + } + + (*fw)->data = malloc(buf_len); + if ((*fw)->data == NULL) { + free(*fw); + return BLADERF_ERR_MEM; + } + + memcpy((*fw)->data, buf, buf_len); + (*fw)->data_len = (uint32_t)buf_len; + + status = scan_fw_sections(*fw); + if (status != 0) { + goto error; + } + + return 0; + +error: + fx3_fw_free(*fw); + return status; +} + +void fx3_fw_free(struct fx3_firmware *fw) +{ + free(fw->data); + free(fw); +} + +bool fx3_fw_next_section(struct fx3_firmware *fw, uint32_t *section_addr, + uint8_t **section_data, uint32_t *section_len) +{ + uint32_t len; + uint32_t addr; + uint8_t *data; + + /* Max offset is the checksum address */ + const uint32_t max_offset = fw->data_len - sizeof(uint32_t); + + assert(fw != NULL); + assert(fw->data != NULL); + + *section_addr = 0; + *section_data = NULL; + *section_len = 0; + + if (fw->curr_section >= fw->num_sections) { + return false; + } + + /* Length in bytes (as converted from 32-bit words) */ + len = to_uint32(fw, fw->section_offset) * sizeof(uint32_t); + if (len == 0) { + return false; + } + + /* Advance to address field */ + fw->section_offset += sizeof(uint32_t); + assert(fw->section_offset < max_offset); + addr = to_uint32(fw, fw->section_offset); + + /* Advance to data field */ + fw->section_offset += sizeof(uint32_t); + assert(fw->section_offset < max_offset); + data = &fw->data[fw->section_offset]; + + /* Advance to the next section for the next call */ + fw->section_offset += len; + assert(fw->section_offset < max_offset); + fw->curr_section++; + + *section_addr = addr; + *section_data = data; + *section_len = len; + return true; +} + +uint32_t fx3_fw_entry_point(const struct fx3_firmware *fw) +{ + assert(fw != NULL); + return fw->entry_addr; +} diff --git a/Radio/HW/BladeRF/src/driver/fx3_fw.h b/Radio/HW/BladeRF/src/driver/fx3_fw.h new file mode 100644 index 0000000..5e715d5 --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/fx3_fw.h @@ -0,0 +1,79 @@ +/* + * This file defines functionality for reading and validating an FX3 firmware + * image, and providing access to the image contents. + * + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2014 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DRIVER_FX3_FW_H_ +#define DRIVER_FX3_FW_H_ + +#include "host_config.h" +#include <stdbool.h> +#include <stdint.h> + +#include "board/board.h" + +struct fx3_firmware; + +/** + * Parse the contents of an FX3 firmware file into a fx3_firmware structure. + * + * @param[out] fw Handle to FX3 firmware data + * @param[in] buf Buffer containing a FX3 firmware image + * @param[in] buf_len Length of buffer + * + * @return 0 on success, BLADERF_ERR_INVAL if image validation fails, + * BLADERF_ERR_* values on other errors. + */ +int fx3_fw_parse(struct fx3_firmware **fw, uint8_t *buf, size_t buf_len); + +/** + * Free the data stored in the provided fx3_firmware structure. + * + * @param[inout] fw Structure to deallocate + */ +void fx3_fw_free(struct fx3_firmware *fw); + +/** + * This function allows each section to be iterated over by calling it + * repeatedly, until it returns false. + * + * @param[in] fw Handle FX3 firmware data + * @param[out] section_addr Target RAM address of the section (on the FX3) + * @param[out] section_data Updated to point to start of next section's data + * @parma[out] section_len Length of the next section + * + * @return true if this function returned section data, false if the end of the + * FW has been reached and no data is available. + */ +bool fx3_fw_next_section(struct fx3_firmware *fw, + uint32_t *section_addr, + uint8_t **section_data, + uint32_t *section_len); + +/** + * @param[in] fw Handle FX3 firmware data + * + * @return The 32-bit little-endian address of the firmware entry point. + */ +uint32_t fx3_fw_entry_point(const struct fx3_firmware *fw); + +#endif diff --git a/Radio/HW/BladeRF/src/driver/ina219.c b/Radio/HW/BladeRF/src/driver/ina219.c new file mode 100644 index 0000000..f3cd23b --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/ina219.c @@ -0,0 +1,154 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2017 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <libbladeRF.h> + +#include "log.h" + +#include "ina219.h" + +#define INA219_REG_CONFIGURATION 0x00 +#define INA219_REG_SHUNT_VOLTAGE 0x01 +#define INA219_REG_BUS_VOLTAGE 0x02 +#define INA219_REG_POWER 0x03 +#define INA219_REG_CURRENT 0x04 +#define INA219_REG_CALIBRATION 0x05 + +int ina219_init(struct bladerf *dev, float r_shunt) +{ + int status; + uint16_t value; + + /* Soft-reset INA219 */ + value = 0x8000; + status = dev->backend->ina219_write(dev, INA219_REG_CONFIGURATION, value); + if (status < 0) { + log_error("INA219 soft reset error: %d\n", status); + return status; + } + + /* Poll until we're out of reset */ + while (value & 0x8000) { + status = dev->backend->ina219_read(dev, INA219_REG_CONFIGURATION, &value); + if (status < 0) { + log_error("INA219 soft reset poll error: %d\n", status); + return status; + } + } + + /* Write configuration register */ + /* BRNG (13) = 0 for 16V FSR + PG (12-11) = 00 for 40mV + BADC (10-7) = 0011 for 12-bit / 532uS + SADC (6-3) = 0011 for 12-bit / 532uS + MODE (2-0) = 111 for continuous shunt & bus */ + value = 0x019f; + status = dev->backend->ina219_write(dev, INA219_REG_CONFIGURATION, value); + if (status < 0) { + log_error("INA219 configuration error: %d\n", status); + return status; + } + + log_debug("Configuration register: 0x%04x\n", value); + + /* Write calibration register */ + /* Current_LSB = 0.001 A / LSB */ + /* Calibration = 0.04096 / (Current_LSB * r_shunt) */ + value = (uint16_t)((0.04096 / (0.001 * r_shunt)) + 0.5); + status = dev->backend->ina219_write(dev, INA219_REG_CALIBRATION, value); + if (status < 0) { + log_error("INA219 calibration error: %d\n", status); + return status; + } + + log_debug("Calibration register: 0x%04x\n", value); + + return 0; +} + +int ina219_read_shunt_voltage(struct bladerf *dev, float *voltage) +{ + int status; + uint16_t data; + + status = dev->backend->ina219_read(dev, INA219_REG_SHUNT_VOLTAGE, &data); + if (status < 0) { + return status; + } + + /* Scale by 1e-5 LSB / Volt */ + *voltage = ((float)((int16_t)data)) * 1e-5F; + + return 0; +} + +int ina219_read_bus_voltage(struct bladerf *dev, float *voltage) +{ + int status; + uint16_t data; + + status = dev->backend->ina219_read(dev, INA219_REG_BUS_VOLTAGE, &data); + if (status < 0) { + return status; + } + + /* If overflow flag is set */ + if (data & 0x1) { + return BLADERF_ERR_UNEXPECTED; + } + + /* Scale by 0.004 LSB / Volt */ + *voltage = ((float)(data >> 3)) * 0.004F; + + return 0; +} + +int ina219_read_current(struct bladerf *dev, float *current) +{ + int status; + uint16_t data; + + status = dev->backend->ina219_read(dev, INA219_REG_CURRENT, &data); + if (status < 0) { + return status; + } + + /* Scale by 0.001 LSB / Ampere */ + *current = ((float)((int16_t)data)) * 0.001F; + + return 0; +} + +int ina219_read_power(struct bladerf *dev, float *power) +{ + int status; + uint16_t data; + + status = dev->backend->ina219_read(dev, INA219_REG_POWER, &data); + if (status < 0) { + return status; + } + + /* Scale by 0.020 LSB / Watt */ + *power = ((float)((int16_t)data)) * 0.020F; + + return 0; +} diff --git a/Radio/HW/BladeRF/src/driver/ina219.h b/Radio/HW/BladeRF/src/driver/ina219.h new file mode 100644 index 0000000..575d0da --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/ina219.h @@ -0,0 +1,83 @@ +/** + * @file ina219.h + * + * @brief INA219 Support + * + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2017 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DRIVER_INA219_H_ +#define DRIVER_INA219_H_ + +#include <libbladeRF.h> + +#include "board/board.h" + +/** + * Initialize the INA219 voltage/current/power monitor. + * + * @param dev Device handle + * @param[in] r_shunt Shunt resistor in ohms + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int ina219_init(struct bladerf *dev, float r_shunt); + +/** + * Read the shunt voltage. + * + * @param dev Device handle + * @param[out] voltage Shunt voltage in volts + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int ina219_read_shunt_voltage(struct bladerf *dev, float *voltage); + +/** + * Read the bus voltage. + * + * @param dev Device handle + * @param[out] voltage Bus voltage in volts + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int ina219_read_bus_voltage(struct bladerf *dev, float *voltage); + +/** + * Read the load current. + * + * @param dev Device handle + * @param[out] current Load current in amps + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int ina219_read_current(struct bladerf *dev, float *current); + +/** + * Read the load power. + * + * @param dev Device handle + * @param[out] power Load power in watts + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int ina219_read_power(struct bladerf *dev, float *power); + +#endif diff --git a/Radio/HW/BladeRF/src/driver/si5338.c b/Radio/HW/BladeRF/src/driver/si5338.c new file mode 100644 index 0000000..80cda11 --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/si5338.c @@ -0,0 +1,669 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2013 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <inttypes.h> +#include <limits.h> + +#include <libbladeRF.h> + +#include "rel_assert.h" +#include "host_config.h" +#include "log.h" + +#include "si5338.h" + +#define SI5338_EN_A 0x01 +#define SI5338_EN_B 0x02 + +#define SI5338_F_VCO (38400000UL * 66UL) + +/** + * This is used set or recreate the si5338 frequency + * Each si5338 multisynth module can be set independently + */ +struct si5338_multisynth { + /* Multisynth to program (0-3) */ + uint8_t index; + + /* Base address of the multisynth */ + uint16_t base; + + /* Requested and actual sample rates */ + struct bladerf_rational_rate requested; + struct bladerf_rational_rate actual; + + /* Enables for A and/or B outputs */ + uint8_t enable; + + /* f_out = fvco / (a + b/c) / r */ + uint32_t a, b, c, r; + + /* (a, b, c) in multisynth (p1, p2, p3) form */ + uint32_t p1, p2, p3; + + /* (p1, p2, p3) in register form */ + uint8_t regs[10]; +}; + +static void si5338_log_read_error(int error, const char *s) +{ + log_debug("Could not read from si5338 (%d): %s\n", error, s); + return; +} + +static void si5338_log_write_error(int error, const char *s) +{ + log_debug("Could not write to si5338 (%d): %s\n", error, s); + return; +} + +static uint64_t si5338_gcd(uint64_t a, uint64_t b) +{ + uint64_t t; + while (b != 0) { + t = b; + b = a % t; + a = t; + } + return a; +} + +static void si5338_rational_reduce(struct bladerf_rational_rate *r) +{ + int64_t val; + + if ((r->den > 0) && (r->num >= r->den)) { + /* Get whole number */ + uint64_t whole = r->num / r->den; + r->integer += whole; + r->num = r->num - whole*r->den; + } + + /* Reduce fraction */ + val = si5338_gcd(r->num, r->den); + if (val > 0) { + r->num /= val; + r->den /= val; + } + + return ; +} + +static void si5338_rational_double(struct bladerf_rational_rate *r) +{ + r->integer *= 2; + r->num *= 2; + si5338_rational_reduce(r); + return; +} + +/** + * Update the base address of the selected multisynth + */ +static void si5338_update_base(struct si5338_multisynth *ms) +{ + ms->base = 53 + ms->index*11 ; + return; +} + +/** + * Unpack the recently read registers into (p1, p2, p3) and (a, b, c) + * + * Precondition: + * regs[10] and r have been read + * + * Post-condition: + * (p1, p2, p3), (a, b, c) and actual are populated + */ +static void si5338_unpack_regs(struct si5338_multisynth *ms) +{ + uint64_t temp; + + /* Zeroize */ + ms->p1 = ms->p2 = ms->p3 = 0; + + /* Populate */ + ms->p1 = ((ms->regs[2]&3)<<16) | (ms->regs[1]<<8) | (ms->regs[0]); + ms->p2 = (ms->regs[5]<<22) | (ms->regs[4]<<14) | (ms->regs[3]<<6) | ((ms->regs[2]>>2)&0x3f); + ms->p3 = ((ms->regs[9]&0x3f)<<24) | (ms->regs[8]<<16) | (ms->regs[7]<<8) | (ms->regs[6]); + + log_verbose("Unpacked P1: 0x%8.8x (%u) P2: 0x%8.8x (%u) P3: 0x%8.8x (%u)\n", + ms->p1, ms->p1, ms->p2, ms->p2, ms->p3, ms->p3); + + /* c = p3 */ + ms->c = ms->p3; + + /* a = (p1+512)/128 + * + * NOTE: The +64 is for rounding purposes. + */ + ms->a = (ms->p1+512)/128; + + /* b = (((p1+512)-128*a)*c + (b % c) + 64)/128 */ + temp = (ms->p1+512)-128*(uint64_t)ms->a; + temp = (temp * ms->c) + ms->p2; + temp = (temp + 64) / 128; + assert(temp <= UINT32_MAX); + ms->b = (uint32_t)temp; + + log_verbose("Unpacked a + b/c: %d + %d/%d\n", ms->a, ms->b, ms->c); + log_verbose("Unpacked r: %d\n", ms->r); +} + +/* + * Pack (a, b, c, r) into (p1, p2, p3) and regs[] + */ +static void si5338_pack_regs(struct si5338_multisynth *ms) +{ + /* Precondition: + * (a, b, c) and r have been populated + * + * Post-condition: + * (p1, p2, p3) and regs[10] are populated + */ + + /* p1 = (a * c + b) * 128 / c - 512 */ + uint64_t temp; + temp = (uint64_t)ms->a * ms->c + ms->b; + temp = temp * 128 ; + temp = temp / ms->c - 512; + assert(temp <= UINT32_MAX); + ms->p1 = (uint32_t)temp; + //ms->p1 = ms->a * ms->c + ms->b; + //ms->p1 = ms->p1 * 128; + //ms->p1 = ms->p1 / ms->c - 512; + + /* p2 = (b * 128) % c */ + temp = (uint64_t)ms->b * 128; + temp = temp % ms->c; + assert(temp <= UINT32_MAX); + ms->p2 = (uint32_t)temp; + + /* p3 = c */ + ms->p3 = ms->c; + + log_verbose("MSx P1: 0x%8.8x (%u) P2: 0x%8.8x (%u) P3: 0x%8.8x (%u)\n", + ms->p1, ms->p1, ms->p2, ms->p2, ms->p3, ms->p3); + + /* Regs */ + ms->regs[0] = ms->p1 & 0xff; + ms->regs[1] = (ms->p1 >> 8) & 0xff; + ms->regs[2] = ((ms->p2 & 0x3f) << 2) | ((ms->p1 >> 16) & 0x3); + ms->regs[3] = (ms->p2 >> 6) & 0xff; + ms->regs[4] = (ms->p2 >> 14) & 0xff; + ms->regs[5] = (ms->p2 >> 22) & 0xff; + ms->regs[6] = ms->p3 & 0xff; + ms->regs[7] = (ms->p3 >> 8) & 0xff; + ms->regs[8] = (ms->p3 >> 16) & 0xff; + ms->regs[9] = (ms->p3 >> 24) & 0xff; + + return ; +} + +static int si5338_write_multisynth(struct bladerf *dev, + struct si5338_multisynth *ms) +{ + int i, status; + uint8_t r_power, r_count, val; + + log_verbose("Writing MS%d\n", ms->index); + + /* Write out the enables */ + status = dev->backend->si5338_read(dev, 36 + ms->index, &val); + if (status < 0) { + si5338_log_read_error(status, bladerf_strerror(status)); + return status; + } + val |= ms->enable; + log_verbose("Wrote enable register: 0x%2.2x\n", val); + status = dev->backend->si5338_write(dev, 36 + ms->index, val); + if (status < 0) { + si5338_log_write_error(status, bladerf_strerror(status)); + return status; + } + + /* Write out the registers */ + for (i = 0 ; i < 10 ; i++) { + status = dev->backend->si5338_write(dev, ms->base + i, *(ms->regs+i)); + if (status < 0) { + si5338_log_write_error(status, bladerf_strerror(status)); + return status; + } + log_verbose("Wrote regs[%d]: 0x%2.2x\n", i, *(ms->regs+i)); + } + + /* Calculate r_power from c_count */ + r_power = 0; + r_count = ms->r >> 1 ; + while (r_count > 0) { + r_count >>= 1; + r_power++; + } + + /* Set the r value to the log2(r_count) to match Figure 18 */ + val = 0xc0; + val |= (r_power<<2); + + log_verbose("Wrote r register: 0x%2.2x\n", val); + + status = dev->backend->si5338_write(dev, 31 + ms->index, val); + if (status < 0) { + si5338_log_write_error(status, bladerf_strerror(status)); + } + + return status ; +} + +static int si5338_read_multisynth(struct bladerf *dev, + struct si5338_multisynth *ms) +{ + int i, status; + uint8_t val; + + log_verbose("Reading MS%d\n", ms->index); + + /* Read the enable bits */ + status = dev->backend->si5338_read(dev, 36 + ms->index, &val); + if (status < 0) { + si5338_log_read_error(status, bladerf_strerror(status)); + return status ; + } + ms->enable = val&7; + log_verbose("Read enable register: 0x%2.2x\n", val); + + /* Read all of the multisynth registers */ + for (i = 0; i < 10; i++) { + status = dev->backend->si5338_read(dev, ms->base + i, ms->regs+i); + if (status < 0) { + si5338_log_read_error(status, bladerf_strerror(status)); + return status; + } + log_verbose("Read regs[%d]: 0x%2.2x\n", i, *(ms->regs+i)); + } + + /* Populate the RxDIV value from the register */ + status = dev->backend->si5338_read(dev, 31 + ms->index, &val); + if (status < 0) { + si5338_log_read_error(status, bladerf_strerror(status)); + return status; + } + /* RxDIV is stored as a power of 2, so restore it on readback */ + log_verbose("Read r register: 0x%2.2x\n", val); + val = (val>>2)&7; + ms->r = (1<<val); + + /* Unpack the regs into appropriate values */ + si5338_unpack_regs(ms) ; + + return 0; +} + +static void si5338_calculate_ms_freq(struct si5338_multisynth *ms, + struct bladerf_rational_rate *rate) +{ + struct bladerf_rational_rate abc; + abc.integer = ms->a; + abc.num = ms->b; + abc.den = ms->c; + + rate->integer = 0; + rate->num = SI5338_F_VCO * abc.den; + rate->den = (uint64_t)ms->r*(abc.integer * abc.den + abc.num); + + /* Compensate for doubling of frequency for LMS sampling clocks */ + if(ms->index == 1 || ms->index == 2) { + rate->den *= 2; + } + + si5338_rational_reduce(rate); + + log_verbose("Calculated multisynth frequency: %" + PRIu64" + %"PRIu64"/%"PRIu64"\n", + rate->integer, rate->num, rate->den); + + return; +} + +static int si5338_calculate_multisynth(struct si5338_multisynth *ms, + struct bladerf_rational_rate *rate) +{ + + struct bladerf_rational_rate req; + struct bladerf_rational_rate abc; + uint8_t r_value; + + /* Don't muss with the users data */ + req = *rate; + + /* Double requested frequency for sample clocks since LMS requires + * 2:1 clock:sample rate + */ + if(ms->index == 1 || ms->index == 2) { + si5338_rational_double(&req); + } + + /* Find a suitable R value */ + r_value = 1; + while (req.integer < 5000000 && r_value < 32) { + si5338_rational_double(&req); + r_value <<= 1; + } + + if (r_value == 32 && req.integer < 5000000) { + log_debug("Sample rate requires r > 32\n"); + return BLADERF_ERR_INVAL; + } else { + log_verbose("Found r value of: %d\n", r_value); + } + + /* Find suitable MS (a, b, c) values */ + abc.integer = 0; + abc.num = SI5338_F_VCO * req.den; + abc.den = req.integer * req.den + req.num; + si5338_rational_reduce(&abc); + + log_verbose("MSx a + b/c: %"PRIu64" + %"PRIu64"/%"PRIu64"\n", + abc.integer, abc.num, abc.den); + + /* Check values to make sure they are OK */ + if (abc.integer < 8) { + switch (abc.integer) { + case 0: + case 1: + case 2: + case 3: + case 5: + case 7: + log_debug("Integer portion too small: %"PRIu64"\n", abc.integer); + return BLADERF_ERR_INVAL; + } + } else if (abc.integer > 567) { + log_debug("Integer portion too large: %"PRIu64"\n", abc.integer); + return BLADERF_ERR_INVAL; + } + + /* Loss of precision if num or den are greater than 2^30-1 */ + while (abc.num > (1<<30) || abc.den > (1<<30) ) { + log_debug("Loss of precision in reducing fraction from " + "%"PRIu64"/%"PRIu64" to %"PRIu64"/%"PRIu64"\n", + abc.num, abc.den, abc.num>>1, abc.den>>1); + abc.num >>= 1; + abc.den >>= 1; + } + + log_verbose("MSx a + b/c: %"PRIu64" + %"PRIu64"/%"PRIu64"\n", + abc.integer, abc.num, abc.den); + + /* Set it in the multisynth */ + assert(abc.integer <= UINT32_MAX); + assert(abc.num <= UINT32_MAX); + assert(abc.den <= UINT32_MAX); + ms->a = (uint32_t)abc.integer; + ms->b = (uint32_t)abc.num; + ms->c = (uint32_t)abc.den; + ms->r = r_value; + + /* Pack the registers */ + si5338_pack_regs(ms); + + return 0; +} + +/** + * Configure a multisynth for either the RX/TX sample clocks (index=1 or 2) + * or for the SMB output (index=3). + */ +static int si5338_set_rational_multisynth(struct bladerf *dev, + uint8_t index, uint8_t channel, + struct bladerf_rational_rate *rate, + struct bladerf_rational_rate *actual_ret) +{ + struct si5338_multisynth ms; + struct bladerf_rational_rate req; + struct bladerf_rational_rate actual; + int status; + + si5338_rational_reduce(rate); + + /* Save off the value */ + req = *rate; + + /* Setup the multisynth enables and index */ + ms.index = index; + ms.enable = channel; + + /* Update the base address register */ + si5338_update_base(&ms); + + /* Calculate multisynth values */ + status = si5338_calculate_multisynth(&ms, &req); + if(status != 0) { + return status; + } + + /* Get the actual rate */ + si5338_calculate_ms_freq(&ms, &actual); + if (actual_ret) { + memcpy(actual_ret, &actual, sizeof(*actual_ret)); + } + + /* Program it to the part */ + status = si5338_write_multisynth(dev, &ms); + + /* Done */ + return status ; +} + + +int si5338_set_rational_sample_rate(struct bladerf *dev, bladerf_channel ch, + const struct bladerf_rational_rate *rate, + struct bladerf_rational_rate *actual) +{ + struct bladerf_rational_rate rate_reduced = *rate; + uint8_t index = (ch == BLADERF_CHANNEL_RX(0)) ? 1 : 2; + uint8_t channel = SI5338_EN_A; + + /* Enforce minimum sample rate */ + si5338_rational_reduce(&rate_reduced); + if (rate_reduced.integer < BLADERF_SAMPLERATE_MIN) { + log_debug("%s: provided sample rate violates minimum\n", __FUNCTION__); + return BLADERF_ERR_INVAL; + } + + if (ch == BLADERF_CHANNEL_TX(0)) { + channel |= SI5338_EN_B; + } + + return si5338_set_rational_multisynth(dev, index, channel, &rate_reduced, actual); +} + +int si5338_set_rational_smb_freq(struct bladerf *dev, + const struct bladerf_rational_rate *rate, + struct bladerf_rational_rate *actual) +{ + struct bladerf_rational_rate rate_reduced = *rate; + + /* Enforce minimum and maximum frequencies */ + si5338_rational_reduce(&rate_reduced); + + if (rate_reduced.integer < BLADERF_SMB_FREQUENCY_MIN) { + log_debug("%s: provided SMB freq violates minimum\n", __FUNCTION__); + return BLADERF_ERR_INVAL; + } else if (rate_reduced.integer > BLADERF_SMB_FREQUENCY_MAX) { + log_debug("%s: provided SMB freq violates maximum\n", __FUNCTION__); + return BLADERF_ERR_INVAL; + } + + return si5338_set_rational_multisynth(dev, 3, SI5338_EN_A, &rate_reduced, actual); +} + +int si5338_set_sample_rate(struct bladerf *dev, bladerf_channel ch, + uint32_t rate, uint32_t *actual) +{ + struct bladerf_rational_rate req, act; + int status; + + memset(&act, 0, sizeof(act)); + log_verbose("Setting integer sample rate: %d\n", rate); + req.integer = rate; + req.num = 0; + req.den = 1; + + status = si5338_set_rational_sample_rate(dev, ch, &req, &act); + + if (status == 0 && act.num != 0) { + log_info("Non-integer sample rate set from integer sample rate, " + "truncating output.\n"); + } + + assert(act.integer <= UINT32_MAX); + + if (actual) { + *actual = (uint32_t)act.integer; + } + log_verbose("Set actual integer sample rate: %d\n", act.integer); + + return status ; +} + +int si5338_set_smb_freq(struct bladerf *dev, uint32_t rate, uint32_t *actual) +{ + struct bladerf_rational_rate req, act; + int status; + + memset(&act, 0, sizeof(act)); + log_verbose("Setting integer SMB frequency: %d\n", rate); + req.integer = rate; + req.num = 0; + req.den = 1; + + status = si5338_set_rational_smb_freq(dev, &req, &act); + + if (status == 0 && act.num != 0) { + log_info("Non-integer SMB frequency set from integer frequency, " + "truncating output.\n"); + } + + assert(act.integer <= UINT32_MAX); + + if (actual) { + *actual = (uint32_t)act.integer; + } + log_verbose("Set actual integer SMB frequency: %d\n", act.integer); + + return status; +} + +int si5338_get_rational_sample_rate(struct bladerf *dev, bladerf_channel ch, + struct bladerf_rational_rate *rate) +{ + + struct si5338_multisynth ms; + int status; + + /* Select the multisynth we want to read */ + ms.index = (ch == BLADERF_CHANNEL_RX(0)) ? 1 : 2; + + /* Update the base address */ + si5338_update_base(&ms); + + /* Readback */ + status = si5338_read_multisynth(dev, &ms); + + if (status) { + si5338_log_read_error(status, bladerf_strerror(status)); + return status; + } + + si5338_calculate_ms_freq(&ms, rate); + + return 0; +} + +int si5338_get_rational_smb_freq(struct bladerf *dev, + struct bladerf_rational_rate *rate) +{ + struct si5338_multisynth ms; + int status; + + /* Select MS3 for the SMB output */ + ms.index = 3; + si5338_update_base(&ms); + + status = si5338_read_multisynth(dev, &ms); + + if (status) { + si5338_log_read_error(status, bladerf_strerror(status)); + return status; + } + + si5338_calculate_ms_freq(&ms, rate); + + return 0; +} + +int si5338_get_sample_rate(struct bladerf *dev, bladerf_channel ch, + unsigned int *rate) +{ + struct bladerf_rational_rate actual; + int status; + + status = si5338_get_rational_sample_rate(dev, ch, &actual); + + if (status) { + si5338_log_read_error(status, bladerf_strerror(status)); + return status; + } + + if (actual.num != 0) { + log_debug("Fractional sample rate truncated during integer sample rate" + "retrieval\n"); + } + + assert(actual.integer <= UINT_MAX); + *rate = (unsigned int)actual.integer; + + return 0; +} + +int si5338_get_smb_freq(struct bladerf *dev, unsigned int *rate) +{ + struct bladerf_rational_rate actual; + int status; + + status = si5338_get_rational_smb_freq(dev, &actual); + + if (status) { + si5338_log_read_error(status, bladerf_strerror(status)); + return status; + } + + if (actual.num != 0) { + log_debug("Fractional SMB frequency truncated during integer SMB" + " frequency retrieval\n"); + } + + assert(actual.integer <= UINT_MAX); + *rate = (unsigned int)actual.integer; + + return 0; +} diff --git a/Radio/HW/BladeRF/src/driver/si5338.h b/Radio/HW/BladeRF/src/driver/si5338.h new file mode 100644 index 0000000..331f8c1 --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/si5338.h @@ -0,0 +1,134 @@ +/** + * @file si5338.h + * + * @brief SI5339 Support + * + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2013 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DRIVER_SI5338_H_ +#define DRIVER_SI5338_H_ + +#include <libbladeRF.h> + +#include "board/board.h" + +/** + * Set the rational sample rate of the specified channel. + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] rate Rational rate requested + * @param[out] actual Rational rate actually set + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int si5338_set_rational_sample_rate(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_rational_rate *rate, + struct bladerf_rational_rate *actual); + +/** + * Get the rational sample rate of the specified channel. + * + * @param dev Device handle + * @param[in] ch Channel + * @param[out] rate Rational rate + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int si5338_get_rational_sample_rate(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_rational_rate *rate); + +/** + * Set the integral sample rate of the specified channel. + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] rate Integral rate requested + * @param[out] actual Integral rate actually set + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int si5338_set_sample_rate(struct bladerf *dev, + bladerf_channel ch, + uint32_t rate, + uint32_t *actual); + +/** + * Get the integral sample rate of the specified channel. + * + * @param dev Device handle + * @param[in] ch Channel + * @param[out] rate Integral rate + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int si5338_get_sample_rate(struct bladerf *dev, + bladerf_channel ch, + unsigned int *rate); + +/** + * Set the rational frequency of the external SMB port. + * + * @param dev Device handle + * @param[in] rate Rational rate requested + * @param[out] actual Rational rate actually set + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int si5338_set_rational_smb_freq(struct bladerf *dev, + const struct bladerf_rational_rate *rate, + struct bladerf_rational_rate *actual); + +/** + * Get the rational sample rate of the external SMB port. + * + * @param dev Device handle + * @param[out] rate Rational rate + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int si5338_get_rational_smb_freq(struct bladerf *dev, + struct bladerf_rational_rate *rate); + +/** + * Set the integral sample rate of the external SMB port. + * + * @param dev Device handle + * @param[in] rate Integral rate requested + * @param[out] actual Integral rate actually set + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int si5338_set_smb_freq(struct bladerf *dev, uint32_t rate, uint32_t *actual); + +/** + Get the integral sample rate of the external SMB port. + * + * @param dev Device handle + * @param[out] rate Integral rate + * + * @return 0 on success, BLADERF_ERR_* value on failure + */ +int si5338_get_smb_freq(struct bladerf *dev, unsigned int *rate); + +#endif diff --git a/Radio/HW/BladeRF/src/driver/smb_clock.c b/Radio/HW/BladeRF/src/driver/smb_clock.c new file mode 100644 index 0000000..65ccc04 --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/smb_clock.c @@ -0,0 +1,211 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2016 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "log.h" + +#include "smb_clock.h" +#include "driver/si5338.h" +#include "board/board.h" + +struct regvals { + uint8_t addr; + uint8_t data; +}; + +/* Power-on defaults with SMB clock port not in use */ +static const struct regvals default_config[] = { + { 6, 0x08 }, + { 28, 0x0b }, + { 29, 0x08 }, + { 30, 0xb0 }, + { 34, 0xe3 }, + { 39, 0x00 }, + + /* Reset Multisynth 3 */ + { 86, 0x00 }, + { 87, 0x00 }, + { 88, 0x00 }, + { 89, 0x00 }, + { 90, 0x00 }, + { 91, 0x00 }, + { 92, 0x00 }, + { 93, 0x00 }, + { 94, 0x00 }, + { 95, 0x00 }, +}; + +static const struct regvals input_config[] = { + { 6, 0x04 }, + { 28, 0x2b }, + { 29, 0x28 }, + { 30, 0xa8 }, +}; + +static const struct regvals output_config[] = { + { 34, 0x22 }, +}; + +static int write_regs(struct bladerf *dev, const struct regvals *reg, size_t n) +{ + size_t i; + int status = 0; + + for (i = 0; i < n && status == 0; i++) { + status = dev->backend->si5338_write(dev, reg[i].addr, reg[i].data); + } + + return status; +} + +static inline int smb_mode_input(struct bladerf *dev) +{ + int status; + uint8_t val; + + status = write_regs(dev, input_config, ARRAY_SIZE(input_config)); + if (status != 0) { + return status; + } + + /* Turn off any SMB connector output */ + status = dev->backend->si5338_read(dev, 39, &val); + if (status != 0) { + return status; + } + + val &= ~(1); + status = dev->backend->si5338_write(dev, 39, val); + if (status != 0) { + return status; + } + + return status; +} + +static inline int smb_mode_output(struct bladerf *dev) +{ + int status; + uint8_t val; + + status = dev->backend->si5338_read(dev, 39, &val); + if (status != 0) { + return status; + } + + val |= 1; + status = dev->backend->si5338_write(dev, 39, val); + if (status != 0) { + return status; + } + + status = write_regs(dev, output_config, ARRAY_SIZE(output_config)); + if (status != 0) { + return status; + } + + return status; +} + +static inline int smb_mode_disabled(struct bladerf *dev) +{ + return write_regs(dev, default_config, ARRAY_SIZE(default_config)); +} + +int smb_clock_set_mode(struct bladerf *dev, bladerf_smb_mode mode) +{ + int status; + + /* Reset initial state */ + status = smb_mode_disabled(dev); + if (status != 0) { + return status; + } + + /* Apply changes */ + switch (mode) { + case BLADERF_SMB_MODE_DISABLED: + break; + + case BLADERF_SMB_MODE_OUTPUT: + status = smb_mode_output(dev); + break; + + case BLADERF_SMB_MODE_INPUT: + status = smb_mode_input(dev); + break; + + default: + log_debug("Invalid SMB clock port mode: %d\n", mode); + return BLADERF_ERR_INVAL; + } + + return status; +} + +/* For simplicity, we just check a few bits that are indicative of our known + * register configurations for our SMB clock port configurations. + * + * Inconsistent register states (or users manually changing registers) may + * cause this function to erroneously report the state. + */ +int smb_clock_get_mode(struct bladerf *dev, bladerf_smb_mode *mode) +{ + int status; + uint8_t val; + + /* Check DRV3_FMT[2:0] for an output configuration */ + status = dev->backend->si5338_read(dev, 39, &val); + if (status != 0) { + return status; + } + + switch (val & 0x7) { + case 0x00: /* No output */ + break; + + case 0x01: /* CLK3A CMOS/SSTL/HSTL - we're outputting a clock */ + *mode = BLADERF_SMB_MODE_OUTPUT; + return 0; + + case 0x02: /* CLK3B CMOS/SSTL/HSTL - used by the XB-200. */ + *mode = BLADERF_SMB_MODE_UNAVAILBLE; + return 0; + + default: + *mode = BLADERF_SMB_MODE_INVALID; + log_debug("Si5338[39] in unexpected state: 0x%02x\n", val); + return BLADERF_ERR_UNSUPPORTED; + } + + /* Check P2DIV_IN[0] for an input configuration */ + status = dev->backend->si5338_read(dev, 28, &val); + if (status != 0) { + return status; + } + + if ((val & (1 << 5)) != 0) { + *mode = BLADERF_SMB_MODE_INPUT; + } else { + *mode = BLADERF_SMB_MODE_DISABLED; + } + + return status; +} diff --git a/Radio/HW/BladeRF/src/driver/smb_clock.h b/Radio/HW/BladeRF/src/driver/smb_clock.h new file mode 100644 index 0000000..c0f206d --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/smb_clock.h @@ -0,0 +1,47 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2016 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DRIVER_SMB_CLOCK_H_ +#define DRIVER_SMB_CLOCK_H_ + +#include <libbladeRF.h> + +/** + * Set the current mode of operation of the SMB clock port + * + * @param dev Device handle + * @param[in] mode Desired mode + * + * @return 0 on success, or a BLADERF_ERR_* value on failure. + */ +int smb_clock_set_mode(struct bladerf *dev, bladerf_smb_mode mode); + +/** + * Get the current mode of operation of the SMB clock port + * + * @param dev Device handle + * @param[out] mode Desired mode + * + * @return 0 on success, or a value from \ref RETCODES list on failure. + */ +int smb_clock_get_mode(struct bladerf *dev, bladerf_smb_mode *mode); + +#endif diff --git a/Radio/HW/BladeRF/src/driver/spi_flash.c b/Radio/HW/BladeRF/src/driver/spi_flash.c new file mode 100644 index 0000000..f0e1a1d --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/spi_flash.c @@ -0,0 +1,134 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2013 Daniel Gröber <dxld AT darkboxed DOT org> + * Copyright (C) 2013 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include <libbladeRF.h> + +#include "rel_assert.h" +#include "log.h" + +#include "spi_flash.h" +#include "board/board.h" + +static inline int check_eb_access(struct bladerf *dev, + uint32_t erase_block, uint32_t count) +{ + if (erase_block >= dev->flash_arch->num_ebs) { + log_debug("Invalid erase block: %u\n", erase_block); + return BLADERF_ERR_INVAL; + } else if (count > dev->flash_arch->num_ebs) { + log_debug("Invalid number of erase blocks: %u\n", count); + return BLADERF_ERR_INVAL; + } else if ((erase_block + count) > dev->flash_arch->num_ebs) { + log_debug("Requested operation extends past end of flash: " + "eb=%u, count=%u\n", erase_block, count); + return BLADERF_ERR_INVAL; + } else { + return 0; + } +} + +static inline int check_page_access(struct bladerf *dev, + uint32_t page, uint32_t count) +{ + if (page >= dev->flash_arch->num_pages) { + log_debug("Invalid page: %u\n", page); + return BLADERF_ERR_INVAL; + } else if (count > dev->flash_arch->num_pages) { + log_debug("Invalid number of pages: %u\n", count); + return BLADERF_ERR_INVAL; + } else if ((page + count) > dev->flash_arch->num_pages) { + log_debug("Requested operation extends past end of flash: " + "page=%u, count=%u\n", page, count); + return BLADERF_ERR_INVAL; + } else { + return 0; + } +} + +int spi_flash_erase(struct bladerf *dev, uint32_t erase_block, uint32_t count) +{ + int status = check_eb_access(dev, erase_block, count); + + if (status == 0) { + status = dev->backend->erase_flash_blocks(dev, erase_block, count); + } + + return status; +} + +int spi_flash_read(struct bladerf *dev, uint8_t *buf, + uint32_t page, uint32_t count) +{ + int status = check_page_access(dev, page, count); + + if (status == 0) { + status = dev->backend->read_flash_pages(dev, buf, page, count); + } + + return status;; +} + +int spi_flash_write(struct bladerf *dev, const uint8_t *buf, + uint32_t page, uint32_t count) +{ + int status = check_page_access(dev, page, count); + + if (status == 0) { + status = dev->backend->write_flash_pages(dev, buf, page, count); + } + + return status; +} + +int spi_flash_verify(struct bladerf *dev, uint8_t *readback_buf, + const uint8_t *expected_buf, uint32_t page, + uint32_t count) +{ + int status = 0; + size_t i; + const size_t len = count * dev->flash_arch->psize_bytes; + + log_info("Verifying %u pages, starting at page %u\n", count, page); + status = spi_flash_read(dev, readback_buf, page, count); + + if (status < 0) { + log_debug("Failed to read from flash: %s\n", bladerf_strerror(status)); + return status; + } + + for (i = 0; i < len; i++) { + if (expected_buf[i] != readback_buf[i]) { + status = BLADERF_ERR_UNEXPECTED; + log_info("Flash verification failed at byte %llu. " + "Read %02x, expected %02x\n", + i, readback_buf[i], expected_buf[i]); + break; + } + } + + return status; +} + diff --git a/Radio/HW/BladeRF/src/driver/spi_flash.h b/Radio/HW/BladeRF/src/driver/spi_flash.h new file mode 100644 index 0000000..c425494 --- /dev/null +++ b/Radio/HW/BladeRF/src/driver/spi_flash.h @@ -0,0 +1,99 @@ +/** + * @file flash.h + * + * @brief Flash conversion and alignment routines + * + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2013 Daniel Gröber <dxld AT darkboxed DOT org> + * Copyright (C) 2013 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DRIVER_SPI_FLASH_H_ +#define DRIVER_SPI_FLASH_H_ + +#include <assert.h> +#include <stdint.h> + +#include <libbladeRF.h> + +/** + * Erase regions of SPI flash + * + * @param dev Device handle + * @param[in] erase_block Erase block to start erasing at + * @param[in] count Number of blocks to erase. + * + * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `erase_block` or + * `count` value, or a value from \ref RETCODES list on other failures + */ +int spi_flash_erase(struct bladerf *dev, uint32_t erase_block, uint32_t count); + +/** + * Read data from flash + * + * @param dev Device handle + * @param[out] buf Buffer to read data into. Must be `count` * + * flash-page-size bytes or larger. + * @param[in] page Page to begin reading from + * @param[in] count Number of pages to read + * + * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `page` or `count` + * value, or a value from \ref RETCODES list on other failures. + */ +int spi_flash_read(struct bladerf *dev, + uint8_t *buf, + uint32_t page, + uint32_t count); + +/** + * Verify data in flash + * + * @param dev Device handle + * @param[out] readback_buf Buffer to read data into. Must be `count` * + * flash-page-size bytes or larger. + * @param[in] expected_buf Expected contents of buffer + * @param[in] page Page to begin reading from + * @param[in] count Number of pages to read + * + * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `page` or `count` + * value, or a value from \ref RETCODES list on other failures. + */ +int spi_flash_verify(struct bladerf *dev, + uint8_t *readback_buf, + const uint8_t *expected_buf, + uint32_t page, + uint32_t count); + +/** + * Write data to flash + * + * @param dev Device handle + * @param[in] buf Data to write to flash + * @param[in] page Page to begin writing at + * @param[in] count Number of pages to write + * + * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `page` or `count` + * value, or a value from \ref RETCODES list on other failures. + */ +int spi_flash_write(struct bladerf *dev, + const uint8_t *buf, + uint32_t page, + uint32_t count); + +#endif |