From 88afee26d7bd708edc4e6a6be20475030d553255 Mon Sep 17 00:00:00 2001
From: Jared Boone <jboone@earfeast.com>
Date: Sun, 5 Aug 2018 12:21:25 -0700
Subject: [PATCH] Clock Manager: Detect Si5351 CLKIN present, measure
 frequency, and use if approximately 10MHz.

---
 firmware/application/clock_manager.cpp | 28 ++++++++++++++++++++++----
 firmware/application/clock_manager.hpp |  2 ++
 firmware/application/si5351.hpp        |  9 +++++++++
 3 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp
index 0cbf462f..fcddba0f 100644
--- a/firmware/application/clock_manager.cpp
+++ b/firmware/application/clock_manager.cpp
@@ -269,11 +269,23 @@ void ClockManager::init() {
 	clock_generator.enable_fanout();
 	clock_generator.set_pll_input_sources(si5351_pll_input_sources);
 
-	const bool use_clkin = false;
+	const auto clkin_present = !clock_generator.clkin_loss_of_signal();
+	auto clkin_valid = false;
+
+	if( clkin_present ) {
+		// Measure Si5351B CLKIN frequency against LPC43xx IRC oscillator
+		set_gp_clkin_to_clkin_direct();
+		start_frequency_monitor_measurement(cgu::CLK_SEL::GP_CLKIN);
+		wait_For_frequency_monitor_measurement_done();
+		const auto clkin_frequency = get_frequency_monitor_measurement_in_hertz();
+
+		// CLKIN is required to be 10MHz. FREQ_MON measurement is accurate to 1.5%
+		// due to LPC43xx IRC oscillator precision.
+		clkin_valid = (clkin_frequency >= 9850000) && (clkin_frequency <= 10150000);
+	}
+
 	clock_generator.set_clock_control(
-		use_clkin ?
-			si5351_clock_control_clkin
-		: si5351_clock_control_xtal
+		clkin_valid ? si5351_clock_control_clkin : si5351_clock_control_xtal
 	);
 
 	clock_generator.write(si5351_pll_a_xtal_reg);
@@ -422,6 +434,14 @@ void ClockManager::disable_gp_clkin_source() {
 	clock_generator.disable_output(clock_generator_output_mcu_clkin);
 }
 
+void ClockManager::set_gp_clkin_to_clkin_direct() {
+	clock_generator.set_clock_control(
+		clock_generator_output_mcu_clkin,
+		{ ClockControl::CLK_IDRV_2mA | ClockControl::CLK_SRC_CLKIN | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_On }
+	);
+	enable_gp_clkin_source();
+}
+
 void ClockManager::start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel) {
 	// Measure a clock input for 480 cycles of the LPC43xx IRC.
 	LPC_CGU->FREQ_MON = LPC_CGU_FREQ_MON_Type {
diff --git a/firmware/application/clock_manager.hpp b/firmware/application/clock_manager.hpp
index de36d374..5d4799ee 100644
--- a/firmware/application/clock_manager.hpp
+++ b/firmware/application/clock_manager.hpp
@@ -77,6 +77,8 @@ private:
 
 	void enable_gp_clkin_source();
 	void disable_gp_clkin_source();
+	void set_gp_clkin_to_clkin_direct();
+
 	void start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel);
 	void wait_For_frequency_monitor_measurement_done();
 
diff --git a/firmware/application/si5351.hpp b/firmware/application/si5351.hpp
index 58104f82..c7465d71 100644
--- a/firmware/application/si5351.hpp
+++ b/firmware/application/si5351.hpp
@@ -310,6 +310,10 @@ public:
 		while(device_status() & 0x80);
 	}
 
+	bool clkin_loss_of_signal() {
+		return (device_status() >> 4) & 1;
+	}
+	
 	void enable_fanout() {
 		write_register(Register::FanoutEnable, 0b11010000);
 	}
@@ -373,6 +377,11 @@ public:
 		update_all_clock_control();
 	}
 
+	void set_clock_control(const size_t n, const ClockControl::Type clock_control) {
+		_clock_control[n] = clock_control;
+		write_register(Register::CLKControl_Base + n, _clock_control[n]);
+	}
+
 	void enable_clock(const size_t n) {
 		_clock_control[n] &= ~ClockControl::CLK_PDN_Mask;
 		write_register(Register::CLKControl_Base + n, _clock_control[n]);
-- 
GitLab