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