From 4b0c2c0c532b35b523d8b41aea623f024eb5d735 Mon Sep 17 00:00:00 2001
From: Jason Tang <jtang613@gmail.com>
Date: Tue, 28 Sep 2021 16:44:31 -0400
Subject: [PATCH 1/3] Add option to set and enable external reference on
 ADF4002. Useful for GPSDO on ref_in.

---
 LimeUtil/LimeUtil.cpp | 76 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 74 insertions(+), 2 deletions(-)

--- a/LimeUtil/LimeUtil.cpp
+++ b/LimeUtil/LimeUtil.cpp
@@ -17,6 +17,9 @@
 #include "LMS64CProtocol.h"
 #include "lms7_device.h"
 #include "device_constants.h"
+#include "ADF4002.h"
+#include <vector>
+#include <stdint.h>
 
 using namespace lime;
 
@@ -59,6 +62,11 @@ static int printHelp(void)
     std::cout << "    --dir[=direction, default=BOTH]    \t Calibration direction, RX, TX, BOTH" << std::endl;
     std::cout << "    --chans[=channels, default=ALL]    \t Calibration channels, 0, 1, ALL" << std::endl;
     std::cout << std::endl;
+    std::cout << "  External reference clock:" << std::endl;
+    std::cout << "    --refclk[=\"module=foo,serial=bar\"] \t Enable external reference clock" << std::endl;
+    std::cout << "    --fref[=freq]                \t Reference frequency (Hz)" << std::endl;
+    std::cout << "    --fvco[=freq]                \t VCO frequency (Hz)" << std::endl;
+    std::cout << std::endl;
     return EXIT_SUCCESS;
 }
 
@@ -155,6 +163,74 @@ static int makeDevice(void)
 }
 
 /***********************************************************************
+ * Enable external reference clock 
+ **********************************************************************/
+#define ADF4002_SPI_INDEX 0x30
+#define PERIPH_INPUT_RD_0 0xc8
+static int enableExtRefClk(const std::string &argStr, const double fref, const double fvco)
+{
+    lime::ADF4002* m_pModule;
+    m_pModule = new lime::ADF4002();
+
+    ConnectionHandle handle(argStr);
+
+    std::cout << "Enable external reference clock on device " << std::endl;
+    auto conn = ConnectionRegistry::makeConnection(handle);
+    if (conn == nullptr)
+    {
+        std::cout << "No available device!" << std::endl;
+        return EXIT_FAILURE;
+    }
+    if (not conn->IsOpen())
+    {
+        std::cout << "Connection not open!" << std::endl;
+        ConnectionRegistry::freeConnection(conn);
+        return EXIT_FAILURE;
+    }
+
+    std::cout << "  Setting fRef: " << fref/1e6 << "MHz, fVco: " << fvco/1e6 << "MHz..." << std::endl;
+    int rCount = 125;
+    int nCount = 384;
+    unsigned char data[12];
+
+    m_pModule->SetDefaults();
+    m_pModule->SetFrefFvco(fref, fvco, rCount, nCount);
+    m_pModule->GetConfig(data);
+
+    std::vector<uint32_t> dataWr;
+    for(int i=0; i<12; i+=3)
+        dataWr.push_back((uint32_t)data[i] << 16 | (uint32_t)data[i+1] << 8 | data[i+2]);
+
+    int status;
+    // ADF4002 needs to be writen 4 values of 24 bits
+    status = conn->TransactSPI(ADF4002_SPI_INDEX, dataWr.data(), nullptr, 4);
+    if (status != 0)
+    {
+        std::cout << "Transaction failed!" << std::endl;
+        ConnectionRegistry::freeConnection(conn);
+        return EXIT_FAILURE;
+    }
+
+    uint16_t adfLocked = 0;
+    status = conn->ReadRegister(PERIPH_INPUT_RD_0, adfLocked); 
+    if (status != 0)
+    {
+        std::cout << "ReadRegister failed!" << std::endl;
+        ConnectionRegistry::freeConnection(conn);
+        return EXIT_FAILURE;
+    }
+    // Bit 2 is the lock state
+    adfLocked = (adfLocked & 0x02) >> 1;
+    std::cout << "  ADF4002 Lock State: " << adfLocked << std::endl;
+
+    std::cout << "  Free connection... " << std::flush;
+    ConnectionRegistry::freeConnection(conn);
+    std::cout << "OK" << std::endl;
+    std::cout << std::endl;
+    return EXIT_SUCCESS;
+}
+
+/***********************************************************************
  * Program update (sync images and flash support)
  **********************************************************************/
 static int programUpdate(const bool force, const std::string &argStr)
@@ -304,12 +380,15 @@ int main(int argc, char *argv[])
         {"bw",      required_argument, 0, 'b'},
         {"dir",     required_argument, 0, 'd'},
         {"chans",   required_argument, 0, 'c'},
+        {"refclk", optional_argument, 0, 'E'},
+        {"fref",    optional_argument, 0, 'R'},
+        {"fvco",    optional_argument, 0, 'V'},
         {0, 0, 0,  0}
     };
 
     std::string argStr, dir("BOTH"), chans("ALL");
-    double start(0.0), stop(0.0), step(1e6), bw(30e6);
-    bool testTiming(false), calSweep(false), update(false), force(false);
+    double start(0.0), stop(0.0), step(1e6), bw(30e6), fref(10.0e6), fvco(30.720e6);
+    bool testTiming(false), calSweep(false), extRef(false), update(false), force(false);
     int long_index = 0;
     int option = 0;
     while ((option = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1)
@@ -338,11 +417,18 @@ int main(int argc, char *argv[])
         case 'd': if (optarg != NULL) dir = optarg; break;
         case 'c': if (optarg != NULL) chans = optarg; break;
         case 'F': force = true; break;
+        case 'E':
+            extRef = true;
+            if (optarg != NULL) argStr = "none," + std::string(optarg);
+            break;
+        case 'R': if (optarg != NULL) fref = std::stod(optarg); break;
+        case 'V': if (optarg != NULL) fvco = std::stod(optarg); break;
         }
     }
 
     if (testTiming) return deviceTestTiming(argStr);
     if (calSweep) return deviceCalSweep(argStr, start, stop, step, bw, dir, chans);
+    if (extRef) return enableExtRefClk(argStr, fref, fvco);
     if (update) return programUpdate(force, argStr);
 
     //unknown or unspecified options, do help...
