summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src/driver/smb_clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/BladeRF/src/driver/smb_clock.c')
-rw-r--r--Radio/HW/BladeRF/src/driver/smb_clock.c211
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;
+}