1 /*
2  * Copyright 2012 Michael Ossmann <mike@ossmann.com>
3  * Copyright 2012 Jared Boone <jared@sharebrained.com>
4  *
5  * This file is part of HackRF.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include "si5351c.h"
24 
25 enum pll_sources active_clock_source = PLL_SOURCE_UNINITIALIZED;
26 
27 /* write to single register */
si5351c_write_single(si5351c_driver_t * const drv,uint8_t reg,uint8_t val)28 void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val)
29 {
30 	const uint8_t data_tx[] = { reg, val };
31 	si5351c_write(drv, data_tx, 2);
32 }
33 
34 /* read single register */
si5351c_read_single(si5351c_driver_t * const drv,uint8_t reg)35 uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg)
36 {
37 	const uint8_t data_tx[] = { reg };
38 	uint8_t data_rx[] = { 0x00 };
39 	i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 1, data_rx, 1);
40 	return data_rx[0];
41 }
42 
43 /*
44  * Write to one or more contiguous registers. data[0] should be the first
45  * register number, one or more values follow.
46  */
si5351c_write(si5351c_driver_t * const drv,const uint8_t * const data,const size_t data_count)47 void si5351c_write(si5351c_driver_t* const drv, const uint8_t* const data, const size_t data_count)
48 {
49 	i2c_bus_transfer(drv->bus, drv->i2c_address, data, data_count, NULL, 0);
50 }
51 
52 /* Disable all CLKx outputs. */
si5351c_disable_all_outputs(si5351c_driver_t * const drv)53 void si5351c_disable_all_outputs(si5351c_driver_t* const drv)
54 {
55 	uint8_t data[] = { 3, 0xFF };
56 	si5351c_write(drv, data, sizeof(data));
57 }
58 
59 /* Turn off OEB pin control for all CLKx */
si5351c_disable_oeb_pin_control(si5351c_driver_t * const drv)60 void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv)
61 {
62 	uint8_t data[] = { 9, 0xFF };
63 	si5351c_write(drv, data, sizeof(data));
64 }
65 
66 /* Power down all CLKx */
si5351c_power_down_all_clocks(si5351c_driver_t * const drv)67 void si5351c_power_down_all_clocks(si5351c_driver_t* const drv)
68 {
69 	uint8_t data[] = { 16
70 	, SI5351C_CLK_POWERDOWN
71 	, SI5351C_CLK_POWERDOWN
72 	, SI5351C_CLK_POWERDOWN
73 	, SI5351C_CLK_POWERDOWN
74 	, SI5351C_CLK_POWERDOWN
75 	, SI5351C_CLK_POWERDOWN
76 	, SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE
77 	, SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE
78 	};
79 	si5351c_write(drv, data, sizeof(data));
80 }
81 
82 /*
83  * Register 183: Crystal Internal Load Capacitance
84  * Reads as 0xE4 on power-up
85  * Set to 8pF based on crystal specs and HackRF One testing
86  */
si5351c_set_crystal_configuration(si5351c_driver_t * const drv)87 void si5351c_set_crystal_configuration(si5351c_driver_t* const drv)
88 {
89 	uint8_t data[] = { 183, 0x80 };
90 	si5351c_write(drv, data, sizeof(data));
91 }
92 
93 /*
94  * Register 187: Fanout Enable
95  * Turn on XO and MultiSynth fanout only.
96  */
si5351c_enable_xo_and_ms_fanout(si5351c_driver_t * const drv)97 void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv)
98 {
99 	uint8_t data[] = { 187, 0xD0 };
100 	si5351c_write(drv, data, sizeof(data));
101 }
102 
103 /*
104  * Register 15: PLL Input Source
105  * CLKIN_DIV=0 (Divide by 1)
106  * PLLA_SRC=0 (XTAL)
107  * PLLB_SRC=1 (CLKIN)
108  */
si5351c_configure_pll_sources(si5351c_driver_t * const drv)109 void si5351c_configure_pll_sources(si5351c_driver_t* const drv)
110 {
111 	uint8_t data[] = { 15, 0x08 };
112 
113 	si5351c_write(drv, data, sizeof(data));
114 }
115 
116 /* MultiSynth NA (PLLA) and NB (PLLB) */
si5351c_configure_pll_multisynth(si5351c_driver_t * const drv)117 void si5351c_configure_pll_multisynth(si5351c_driver_t* const drv)
118 {
119 	/*PLLA: 25MHz XTAL * (0x0e00+512)/128 = 800mhz -> int mode */
120 	uint8_t data[] = { 26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00 };
121 	si5351c_write(drv, data, sizeof(data));
122 
123 	/*PLLB: 10MHz CLKIN * (0x2600+512)/128 = 800mhz */
124 	data[0] = 34;
125 	data[4] = 0x26;
126 	si5351c_write(drv, data, sizeof(data));
127 }
128 
si5351c_reset_pll(si5351c_driver_t * const drv)129 void si5351c_reset_pll(si5351c_driver_t* const drv)
130 {
131 	/* reset PLLA and PLLB */
132 	uint8_t data[] = { 177, 0xA0 };
133 	si5351c_write(drv, data, sizeof(data));
134 }
135 
si5351c_configure_multisynth(si5351c_driver_t * const drv,const uint_fast8_t ms_number,const uint32_t p1,const uint32_t p2,const uint32_t p3,const uint_fast8_t r_div)136 void si5351c_configure_multisynth(si5351c_driver_t* const drv,
137 		const uint_fast8_t ms_number,
138 		const uint32_t p1, const uint32_t p2, const uint32_t p3,
139     	const uint_fast8_t r_div)
140 {
141 	/*
142 	 * TODO: Check for p3 > 0? 0 has no meaning in fractional mode?
143 	 * And it makes for more jitter in integer mode.
144 	 */
145 	/*
146 	 * r is the r divider value encoded:
147 	 *   0 means divide by 1
148 	 *   1 means divide by 2
149 	 *   2 means divide by 4
150 	 *   ...
151 	 *   7 means divide by 128
152 	 */
153 	const uint_fast8_t register_number = 42 + (ms_number * 8);
154 	uint8_t data[] = {
155 			register_number,
156 			(p3 >> 8) & 0xFF,
157 			(p3 >> 0) & 0xFF,
158 			(r_div << 4) | (0 << 2) | ((p1 >> 16) & 0x3),
159 			(p1 >> 8) & 0xFF,
160 			(p1 >> 0) & 0xFF,
161 			(((p3 >> 16) & 0xF) << 4) | (((p2 >> 16) & 0xF) << 0),
162 			(p2 >> 8) & 0xFF,
163 			(p2 >> 0) & 0xFF };
164 	si5351c_write(drv, data, sizeof(data));
165 }
166 
si5351c_configure_clock_control(si5351c_driver_t * const drv,const enum pll_sources source)167 void si5351c_configure_clock_control(si5351c_driver_t* const drv, const enum pll_sources source)
168 {
169 	uint8_t pll;
170 #ifdef RAD1O
171 	(void) source;
172 	/* PLLA on XTAL */
173 	pll = SI5351C_CLK_PLL_SRC_A;
174 #endif
175 
176 #if (defined JAWBREAKER || defined HACKRF_ONE)
177 	if (source == PLL_SOURCE_CLKIN) {
178 		/* PLLB on CLKIN */
179 		pll = SI5351C_CLK_PLL_SRC_B;
180 	} else {
181 		/* PLLA on XTAL */
182 		pll = SI5351C_CLK_PLL_SRC_A;
183 	}
184 #endif
185 	/* Clock to CPU is deactivated as it is not used and creates noise */
186 	/* External clock output is deactivated as it is not used and creates noise */
187 	uint8_t data[] = {16
188 	,SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA)
189 	,SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) | SI5351C_CLK_INV
190 	,SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA)
191 	,SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE /*not connected, but: plla int mode*/
192 	,SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA) | SI5351C_CLK_INV
193 	,SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA)
194 	,SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE /*not connected, but: plla int mode*/
195 	,SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE /*not connected, but: plla int mode*/
196 	 };
197 	si5351c_write(drv, data, sizeof(data));
198 }
199 
200 #define SI5351C_CLK_ENABLE(x)	(0<<x)
201 #define SI5351C_CLK_DISABLE(x)	(1<<x)
202 #define SI5351C_REG_OUTPUT_EN	(3)
203 #define SI5351C_REG_CLK3_CTRL	(19)
204 
si5351c_enable_clock_outputs(si5351c_driver_t * const drv)205 void si5351c_enable_clock_outputs(si5351c_driver_t* const drv)
206 {
207 	/* Enable CLK outputs 0, 1, 2, 4, 5 only. */
208 	/* 7: Clock to CPU is deactivated as it is not used and creates noise */
209 	/* 3: External clock output is deactivated by default */
210 	// uint8_t data[] = { 3, ~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 4) | (1 << 5))};
211 	uint8_t data[] = { SI5351C_REG_OUTPUT_EN,
212 		SI5351C_CLK_ENABLE(0) |
213 		SI5351C_CLK_ENABLE(1) |
214 		SI5351C_CLK_ENABLE(2) |
215 		SI5351C_CLK_DISABLE(3) |
216 		SI5351C_CLK_ENABLE(4) |
217 		SI5351C_CLK_ENABLE(5) |
218 		SI5351C_CLK_DISABLE(6) |
219 		SI5351C_CLK_DISABLE(7)
220 	};
221 	si5351c_write(drv, data, sizeof(data));
222 }
223 
si5351c_set_int_mode(si5351c_driver_t * const drv,const uint_fast8_t ms_number,const uint_fast8_t on)224 void si5351c_set_int_mode(si5351c_driver_t* const drv, const uint_fast8_t ms_number, const uint_fast8_t on){
225   uint8_t data[] = {16, 0};
226 
227   if(ms_number < 8){
228       data[0] = 16 + ms_number;
229       data[1] = si5351c_read_single(drv, data[0]);
230 
231       if(on)
232           data[1] |= SI5351C_CLK_INT_MODE;
233       else
234           data[1] &= ~(SI5351C_CLK_INT_MODE);
235 
236       si5351c_write(drv, data, 2);
237   }
238 }
239 
si5351c_set_clock_source(si5351c_driver_t * const drv,const enum pll_sources source)240 void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source)
241 {
242 	if( source != active_clock_source ) {
243 		si5351c_configure_clock_control(drv, source);
244 		active_clock_source = source;
245 	}
246 }
247 
si5351c_clkin_signal_valid(si5351c_driver_t * const drv)248 bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) {
249 	return (si5351c_read_single(drv, 0) & SI5351C_LOS) == 0;
250 }
251 
si5351c_clkout_enable(si5351c_driver_t * const drv,uint8_t enable)252 void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable)
253 {
254 	/* Set optput in output enable register */
255 	uint8_t output_enable = si5351c_read_single(drv, 3);
256 	output_enable = output_enable & !SI5351C_CLK_DISABLE(3);
257 	if(enable)
258 		output_enable = output_enable | SI5351C_CLK_ENABLE(3);
259 	else
260 		output_enable = output_enable | SI5351C_CLK_DISABLE(3);
261 	uint8_t oe_data[] = {SI5351C_REG_OUTPUT_EN, output_enable};
262 	si5351c_write(drv, oe_data, 2);
263 
264 	/* Configure clock to 10MHz (TODO customisable?) */
265 	si5351c_configure_multisynth(drv, 3, 80*128-512, 0, 1, 0);
266 
267 	/* Set power up/doen in CLK3 control register*/
268 	uint8_t pll;
269 	#ifdef RAD1O
270 		/* PLLA on XTAL */
271 		pll = SI5351C_CLK_PLL_SRC_A;
272 	#endif
273 
274 	#if (defined JAWBREAKER || defined HACKRF_ONE)
275 		if (active_clock_source == PLL_SOURCE_CLKIN) {
276 			/* PLLB on CLKIN */
277 			pll = SI5351C_CLK_PLL_SRC_B;
278 		} else {
279 			/* PLLA on XTAL */
280 			pll = SI5351C_CLK_PLL_SRC_A;
281 		}
282 	#endif
283 	uint8_t clk3_ctrl;
284 	if(enable)
285 		clk3_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) | SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA);
286 	else
287 		clk3_ctrl = SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE;
288 	uint8_t clk3_data[] = {SI5351C_REG_CLK3_CTRL, clk3_ctrl};
289 	si5351c_write(drv, clk3_data, 2);
290 }
291