diff options
Diffstat (limited to 'Radio/HW/BladeRF/src/driver/smb_clock.c')
-rw-r--r-- | Radio/HW/BladeRF/src/driver/smb_clock.c | 211 |
1 files changed, 211 insertions, 0 deletions
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; +} |