diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index 0f0085c2..e91e3822 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -111,9 +111,9 @@ void clock_gen_init(void) sample_rate_set(SR_FP_MHZ(10), true); si5351c_configure_clock_control(&si5351c); - si5351c_set_clock_source(&si5351c, PLL_SOURCE_XTAL); + si5351c_change_input(&si5351c, SI5351C_INPUT_XTAL); // soft reset - si5351c_reset_pll(&si5351c, SI5351C_PLL_BOTH); + si5351c_reset_plls(&si5351c, SI5351C_PLL_MASK_BOTH); si5351c_enable_clock_outputs(&si5351c); } @@ -159,9 +159,10 @@ clock_source_t activate_best_clock_source(void) /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ } - si5351c_set_clock_source( - &si5351c, - (source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN); + si5351c_input_t input = (source == CLOCK_SOURCE_HACKRF) ? SI5351C_INPUT_XTAL : + SI5351C_INPUT_CLKIN; + si5351c_change_input(&si5351c, input); + hackrf_ui()->set_clock_source(source); return source; @@ -411,7 +412,7 @@ fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program) } /* Reset PLL to synchronize output clock phase. */ - si5351c_reset_pll(&si5351c, SI5351C_PLL_A); + si5351c_reset_plls(&si5351c, SI5351C_PLL_MASK_A); } #endif diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index a7b605f5..1f27d8db 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * * This file is part of HackRF. @@ -22,6 +22,7 @@ #include #include +#include #include "clock_io.h" #include "delay.h" @@ -36,6 +37,8 @@ #include "platform_scu.h" #endif +#include "si5351c_regs.def" + /* Driver instance. */ // const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { // .duty_cycle_count = 15, @@ -48,16 +51,20 @@ si5351c_driver_t si5351c = { .i2c_address = 0x60, }; -static enum pll_sources active_clock_source = PLL_SOURCE_UNINITIALIZED; +static bool input_initialized = false; +static si5351c_input_t active_input; /* External clock output default is deactivated as it creates noise */ static bool clkout_enabled = false; -static uint8_t outputs_disabled = 0xff; /* write to single register */ void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val) { const uint8_t data_tx[] = {reg, val}; - si5351c_write(drv, data_tx, 2); + i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 2, NULL, 0); + if (reg < SI5351C_CACHED_REGS) { + drv->regs[reg] = val; + si5351c_reg_set_clean(drv, reg); + } } /* read single register */ @@ -66,72 +73,100 @@ uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg) const uint8_t data_tx[] = {reg}; uint8_t data_rx[] = {0x00}; i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 1, data_rx, 1); - return data_rx[0]; + uint8_t val = data_rx[0]; + if (reg < SI5351C_CACHED_REGS) { + drv->regs[reg] = val; + si5351c_reg_set_clean(drv, reg); + } + return val; } -/* - * Write to one or more contiguous registers. data[0] should be the first - * register number, one or more values follow. - */ -void si5351c_write( - si5351c_driver_t* const drv, - const uint8_t* const data, - const size_t data_count) +/* Commit changes to register values. */ +void si5351c_regs_commit(si5351c_driver_t* drv) { - i2c_bus_transfer(drv->bus, drv->i2c_address, data, data_count, NULL, 0); + for (int start = 0; start < SI5351C_CACHED_REGS; start++) { + if (si5351c_reg_is_dirty(drv, start)) { + if (start == SI5351C_CACHED_REGS - 1) { + si5351c_write_single(drv, start, drv->regs[start]); + } else { + int end; + for (end = start + 1; end < 256; end++) { + if (!si5351c_reg_is_dirty(drv, end)) + break; + } + size_t len = 1 + (end - start); + uint8_t data_tx[len]; + data_tx[0] = start; + for (int i = 0; i < (end - start); i++) { + data_tx[1 + i] = drv->regs[start + i]; + } + i2c_bus_transfer( + drv->bus, + drv->i2c_address, + data_tx, + len, + NULL, + 0); + for (int i = start; i < end; i++) { + si5351c_reg_set_clean(drv, i); + } + start = end; + } + } + } + + /* Reset bits are self-clearing. */ + drv->regs[177] = 0; } /* Disable all CLKx outputs. */ void si5351c_disable_all_outputs(si5351c_driver_t* const drv) { - outputs_disabled = 0xff; - uint8_t data[] = {3, outputs_disabled}; - si5351c_write(drv, data, sizeof(data)); + set_all_CLK_OEB(drv, SI5351C_OUTPUT_DISABLE); + si5351c_regs_commit(drv); } /* Disable all CLKx outputs using selected PLL. */ -void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_t pll) +void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_mask_t mask) { - /* - * Bitmask defines outputs using PLL B. Other outputs are assumed to - * use PLL A. - */ - uint8_t pllb_outputs = 0x00; - if (detected_platform() == BOARD_ID_PRALINE) { - pllb_outputs = 0x30; + /* For each CLKx output, check if it is using the specified PLL. */ + for (int op = 0; op < 8; op++) { + int ms; + /* First check which multisynth is used by this output. */ + switch (get_CLK_SRC(drv, op)) { + case SI5351C_SRC_MULTISYNTH_SELF: + ms = op; + break; + case SI5351C_SRC_MULTISYNTH_0_4: + ms = (op < 4) ? 0 : 4; + break; + default: + /* This output is not using any PLL */ + continue; + } + /* Now check which PLL is used by that multisynth. */ + if (mask & (1 << get_MS_SRC(drv, ms))) { + /* This output depends on the specified PLL; disable it. */ + set_CLK_OEB(drv, op, SI5351C_OUTPUT_DISABLE); + } } - - if (pll & SI5351C_PLL_A) { - outputs_disabled |= ~pllb_outputs; - } - if (pll & SI5351C_PLL_B) { - outputs_disabled |= pllb_outputs; - } - uint8_t data[] = {3, outputs_disabled}; - si5351c_write(drv, data, sizeof(data)); + si5351c_regs_commit(drv); } /* Turn off OEB pin control for all CLKx */ void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv) { - uint8_t data[] = {9, 0xFF}; - si5351c_write(drv, data, sizeof(data)); + set_all_OEB_MASK(drv, true); + si5351c_regs_commit(drv); } /* Power down all CLKx */ void si5351c_power_down_all_clocks(si5351c_driver_t* const drv) { - uint8_t data[] = { - 16, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN, - SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE, - SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE}; - si5351c_write(drv, data, sizeof(data)); + set_all_CLK_PDN(drv, true); + set_FBA_INT(drv, true); + set_FBB_INT(drv, true); + si5351c_regs_commit(drv); } /* @@ -141,8 +176,8 @@ void si5351c_power_down_all_clocks(si5351c_driver_t* const drv) */ void si5351c_set_crystal_configuration(si5351c_driver_t* const drv) { - uint8_t data[] = {183, 0x80}; - si5351c_write(drv, data, sizeof(data)); + set_XTAL_CL(drv, SI5351C_XTAL_8PF); + si5351c_regs_commit(drv); } /* @@ -151,8 +186,10 @@ void si5351c_set_crystal_configuration(si5351c_driver_t* const drv) */ void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv) { - uint8_t data[] = {187, 0xD0}; - si5351c_write(drv, data, sizeof(data)); + set_CLKIN_FANOUT_EN(drv, true); + set_XO_FANOUT_EN(drv, true); + set_MS_FANOUT_EN(drv, true); + si5351c_regs_commit(drv); } /* @@ -160,50 +197,39 @@ void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv) * CLKIN_DIV=0 (Divide by 1) * Set both PLLA_SRC and PLLB_SRC */ -void si5351c_configure_pll_sources( - si5351c_driver_t* const drv, - const enum pll_sources source) +void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input) { - uint8_t data[] = {15, 0x00}; - - if (source == PLL_SOURCE_CLKIN) { - data[1] = 0x0c; - } - si5351c_write(drv, data, sizeof(data)); + set_CLKIN_DIV(drv, SI5351C_DIV_1); + set_PLLA_SRC(drv, input); + set_PLLB_SRC(drv, input); + si5351c_regs_commit(drv); } /* MultiSynth NA (PLLA) and NB (PLLB) */ void si5351c_configure_pll_multisynth( si5351c_driver_t* const drv, - const enum pll_sources source) + const si5351c_input_t input) { - /* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */ - uint8_t data[] = {26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00}; - if (source == PLL_SOURCE_CLKIN) { - /* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */ - data[4] = 0x26; + for (si5351c_pll_t pll = SI5351C_PLL_A; pll <= SI5351C_PLL_B; pll++) { + if (input == SI5351C_INPUT_CLKIN) { + /* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */ + set_MSN_P1(drv, pll, 0x2600); + } else { + /* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */ + set_MSN_P1(drv, pll, 0x0E00); + } + set_MSN_P2(drv, pll, 0); + set_MSN_P3(drv, pll, 1); } - si5351c_write(drv, data, sizeof(data)); - - /* Apply same configuration to PLL B. */ - data[0] = 34; - si5351c_write(drv, data, sizeof(data)); + si5351c_regs_commit(drv); } -void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll) +void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask) { - uint8_t value = 0; - - if (pll & SI5351C_PLL_A) { - value |= 0x20; - } - if (pll & SI5351C_PLL_B) { - value |= 0x80; - } - - si5351c_disable_pll_outputs(drv, pll); - uint8_t data[] = {177, value}; - si5351c_write(drv, data, sizeof(data)); + si5351c_disable_pll_outputs(drv, mask); + set_PLLA_RST(drv, (mask & SI5351C_PLL_MASK_A) ? true : false); + set_PLLB_RST(drv, (mask & SI5351C_PLL_MASK_B) ? true : false); + si5351c_regs_commit(drv); delay_us_at_mhz(2000, 204); si5351c_enable_clock_outputs(drv); } @@ -228,110 +254,166 @@ void si5351c_configure_multisynth( * ... * 7 means divide by 128 */ - const uint_fast8_t register_number = 42 + (ms_number * 8); - uint8_t data[] = { - register_number, - (p3 >> 8) & 0xFF, - (p3 >> 0) & 0xFF, - (r_div << 4) | (0 << 2) | ((p1 >> 16) & 0x3), - (p1 >> 8) & 0xFF, - (p1 >> 0) & 0xFF, - (((p3 >> 16) & 0xF) << 4) | (((p2 >> 16) & 0xF) << 0), - (p2 >> 8) & 0xFF, - (p2 >> 0) & 0xFF}; - si5351c_write(drv, data, sizeof(data)); + set_MS_P1(drv, ms_number, p1); + set_MS_P2(drv, ms_number, p2); + set_MS_P3(drv, ms_number, p3); + set_R_DIV(drv, ms_number, r_div); + si5351c_regs_commit(drv); } void si5351c_configure_clock_control(si5351c_driver_t* const drv) { - uint8_t clkout_ctrl; + si5351c_clk_ctrl_t clkout = { + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_8MA, + }; - if (clkout_enabled) { - clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA); - } else { - clkout_ctrl = SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE; + si5351c_clk_ctrl_t powered_down = { + .power_down = true, + .mode = SI5351C_MODE_INT, + }; + + if (!clkout_enabled) { + clkout = powered_down; } + si5351c_clk_ctrl_t clk[8]; + /* Clock to CPU is deactivated as it is not used and creates noise */ /* External clock output is kept in current state */ - uint8_t data[] = { - 16, - SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA), - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) | SI5351C_CLK_INV, - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA), - clkout_ctrl, - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA) | SI5351C_CLK_INV, - SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA), - SI5351C_CLK_POWERDOWN | - SI5351C_CLK_INT_MODE, /* not connected, but: PLL A int mode */ - SI5351C_CLK_POWERDOWN | - SI5351C_CLK_INT_MODE /* not connected, but: PLL B int mode */ + + /* CLK0: MAX5864/CPLD */ + clk[0] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_8MA, }; + /* CLK1: CPLD */ + clk[1] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_0_4, + .drive = SI5351C_DRIVE_2MA, + .invert = true, + }; + /* CLK2: SGPIO */ + clk[2] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_0_4, + .drive = SI5351C_DRIVE_2MA, + }; + /* CLK3: CLKOUT */ + clk[3] = clkout; + /* CLK4: RFFC5072 (MAX2837 on rad1o) */ + clk[4] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_6MA, + .invert = true, + }; + /* CLK5: MAX2837 (MAX2871 on rad1o) */ + clk[5] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; + /* CLK6: none */ + clk[6] = powered_down; + /* CLK7: LPC43xx */ + clk[7] = powered_down; + #ifdef IS_H1_R9 if (IS_H1_R9) { - data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA); - data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); - data[3] = clkout_ctrl; - data[4] = SI5351C_CLK_POWERDOWN; - data[5] = SI5351C_CLK_POWERDOWN; - data[6] = SI5351C_CLK_POWERDOWN; + /* CLK0: MAX5864/CPLD/SGPIO (sample clocks) */ + clk[0] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_6MA, + }; + /* CLK1: RFFC5072/MAX2839 */ + clk[1] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; + /* CLK2: CLKOUT/LPC4320 */ + clk[2] = clkout; + /* Other outputs not present on Si5351A */ + clk[3] = powered_down; + clk[4] = powered_down; + clk[5] = powered_down; + clk[6] = powered_down; + clk[7] = powered_down; } #endif #ifdef IS_PRALINE if (IS_PRALINE) { /* CLK0: AFE_CLK */ - data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); + clk[0] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; /* CLK1: SCT_CLK and FPGA_CLK */ - data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA); + clk[1] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_2MA, + }; /* CLK3: CLKOUT */ - clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA); + clkout.pll = SI5351C_PLL_B; + clk[3] = clkout; /* CLK4: XCVR_CLK */ - data[5] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA) | SI5351C_CLK_INV; - data[6] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA); + clk[4] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_B, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + .invert = true, + }; + /* CLK5: MIX_CLK */ + clk[5] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_INT, + .pll = SI5351C_PLL_B, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_4MA, + }; if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - data[3] = SI5351C_CLK_FRAC_MODE | - SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) | - SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) | - SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA); + clk[2] = (si5351c_clk_ctrl_t){ + .mode = SI5351C_MODE_FRAC, + .pll = SI5351C_PLL_A, + .source = SI5351C_SRC_MULTISYNTH_SELF, + .drive = SI5351C_DRIVE_2MA, + }; } else { /* CLK2: MCU_CLK */ - data[3] = SI5351C_CLK_POWERDOWN; + clk[2] = powered_down; } } #endif - si5351c_write(drv, data, sizeof(data)); -} -#define SI5351C_CLK_ENABLE(x) (0 << x) -#define SI5351C_CLK_DISABLE(x) (1 << x) -#define SI5351C_REG_OUTPUT_EN (3) + for (int i = 0; i < 8; i++) { + set_CLK_PDN(drv, i, clk[i].power_down); + set_MS_INT(drv, i, clk[i].mode); + set_MS_SRC(drv, i, clk[i].pll); + set_CLK_SRC(drv, i, clk[i].source); + set_CLK_IDRV(drv, i, clk[i].drive); + set_CLK_INV(drv, i, clk[i].invert); + } + + si5351c_regs_commit(drv); +} void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) { @@ -341,46 +423,61 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv) /* 3: External clock output is deactivated by default */ uint8_t clkout = 3; - uint8_t value = 0; + + bool enable[8]; #ifdef IS_PRALINE if (IS_PRALINE) { - value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | - SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7); + enable[0] = true; + enable[1] = true; if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) { /* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */ - value |= SI5351C_CLK_ENABLE(2); + enable[2] = true; } else { - value |= SI5351C_CLK_DISABLE(2); + enable[2] = false; } + enable[3] = false; + enable[4] = true; + enable[5] = true; + enable[6] = false; + enable[7] = false; } #endif #ifdef IS_NOT_PRALINE if (IS_NOT_PRALINE) { - value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_ENABLE(2) | SI5351C_CLK_ENABLE(4) | - SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) | - SI5351C_CLK_DISABLE(7); + enable[0] = true; + enable[1] = true; + enable[2] = true; + enable[3] = false; + enable[4] = true; + enable[5] = true; + enable[6] = false; + enable[7] = false; /* HackRF One r9 has only three clock generator outputs. */ #ifdef IS_H1_R9 if (IS_H1_R9) { clkout = 2; - value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) | - SI5351C_CLK_DISABLE(3) | SI5351C_CLK_DISABLE(4) | - SI5351C_CLK_DISABLE(5) | SI5351C_CLK_DISABLE(6) | - SI5351C_CLK_DISABLE(7); + enable[0] = true; + enable[1] = true; + enable[3] = false; + enable[4] = false; + enable[5] = false; + enable[6] = false; + enable[7] = false; } #endif } #endif + enable[clkout] = clkout_enabled; - value |= (clkout_enabled) ? SI5351C_CLK_ENABLE(clkout) : - SI5351C_CLK_DISABLE(clkout); - uint8_t data[] = {SI5351C_REG_OUTPUT_EN, value}; - si5351c_write(drv, data, sizeof(data)); - outputs_disabled = value; + for (int i = 0; i < 8; i++) { + set_CLK_OEB( + drv, + i, + enable[i] ? SI5351C_OUTPUT_ENABLE : SI5351C_OUTPUT_DISABLE); + } + si5351c_regs_commit(drv); #ifdef IS_H1_R9 if (IS_H1_R9) { @@ -399,25 +496,13 @@ void si5351c_set_int_mode( const uint_fast8_t ms_number, const uint_fast8_t on) { - uint8_t data[] = {16, 0}; - - if (ms_number < 8) { - data[0] = 16 + ms_number; - data[1] = si5351c_read_single(drv, data[0]); - - if (on) { - data[1] |= SI5351C_CLK_INT_MODE; - } else { - data[1] &= ~(SI5351C_CLK_INT_MODE); - } - - si5351c_write(drv, data, 2); - } + set_MS_INT(drv, ms_number, on); + si5351c_regs_commit(drv); } -void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source) +void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input) { - if (source == active_clock_source) { + if (input_initialized && input == active_input) { return; } si5351c_disable_all_outputs(drv); @@ -427,8 +512,8 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source * HackRF One r9 always uses PLL A on the XTAL input * but externally switches that input to CLKIN. */ - si5351c_configure_pll_sources(drv, PLL_SOURCE_XTAL); - if (source == PLL_SOURCE_CLKIN) { + si5351c_configure_inputs(drv, SI5351C_INPUT_XTAL); + if (input == SI5351C_INPUT_CLKIN) { gpio_set(platform_gpio()->h1r9_clkin_en); } else { gpio_clear(platform_gpio()->h1r9_clkin_en); @@ -437,12 +522,13 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source #endif #ifdef IS_NOT_H1_R9 if (IS_NOT_H1_R9) { - si5351c_configure_pll_sources(drv, source); + si5351c_configure_inputs(drv, input); } #endif - si5351c_configure_pll_multisynth(drv, source); - active_clock_source = source; - si5351c_reset_pll(drv, SI5351C_PLL_BOTH); + si5351c_configure_pll_multisynth(drv, input); + active_input = input; + input_initialized = true; + si5351c_reset_plls(drv, SI5351C_PLL_MASK_BOTH); } bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) @@ -451,7 +537,9 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) uint32_t f = clkin_frequency(); return (f > 9000000) && (f < 11000000); } else { - return (si5351c_read_single(drv, 0) & SI5351C_LOS) == 0; + si5351c_read_single(drv, LOS_CLKIN); + bool los_clkin = get_LOS_CLKIN(drv); + return !los_clkin; } } @@ -475,7 +563,8 @@ void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable) void si5351c_init(si5351c_driver_t* const drv) { /* Read revision ID */ - selftest.si5351_rev_id = si5351c_read_single(drv, 0) & SI5351C_REVID; + si5351c_read_single(drv, REVID); + selftest.si5351_rev_id = get_REVID(drv); /* Read back interrupt status mask register, flip the mask bits and verify. */ uint8_t int_mask = si5351c_read_single(drv, 2); @@ -494,6 +583,17 @@ void si5351c_init(si5351c_driver_t* const drv) selftest.report.pass = false; } + /* Cache all current register values. */ + uint8_t data_tx[] = {0}; + i2c_bus_transfer( + drv->bus, + drv->i2c_address, + data_tx, + 1, + drv->regs, + sizeof(drv->regs)); + memset(drv->regs_dirty, 0, sizeof(drv->regs_dirty)); + #ifdef IS_H1_R9 if (IS_H1_R9) { const platform_gpio_t* gpio = platform_gpio(); @@ -529,8 +629,6 @@ void si5351c_set_phase( const uint8_t ms_number, const uint8_t offset) { - const uint8_t address = 165 + ms_number; - if (ms_number < 8) { - si5351c_write_single(drv, address, offset & 0x7f); - } + set_CLK_PHOFF(drv, ms_number, offset); + si5351c_regs_commit(drv); } diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h index b38eac90..5be31788 100644 --- a/firmware/common/si5351c.h +++ b/firmware/common/si5351c.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * * This file is part of HackRF. @@ -28,65 +28,93 @@ extern "C" { #include #include -#include #include "i2c_bus.h" #include "i2c_lpc.h" #define SI_INTDIV(x) (x * 128 - 512) -#define SI5351C_CLK_POWERDOWN (1 << 7) -#define SI5351C_CLK_INT_MODE (1 << 6) -#define SI5351C_CLK_FRAC_MODE (0 << 6) - -#define SI5351C_CLK_INV (1 << 4) - -#define SI5351C_CLK_SRC(x) (x << 2) -#define SI5351C_CLK_SRC_XTAL 0 -#define SI5351C_CLK_SRC_CLKIN 1 -#define SI5351C_CLK_SRC_MULTISYNTH_0_4 2 -#define SI5351C_CLK_SRC_MULTISYNTH_SELF 3 - -#define SI5351C_CLK_IDRV(x) (x << 0) -#define SI5351C_CLK_IDRV_2MA 0 -#define SI5351C_CLK_IDRV_4MA 1 -#define SI5351C_CLK_IDRV_6MA 2 -#define SI5351C_CLK_IDRV_8MA 3 - -#define SI5351C_LOS (1 << 4) -#define SI5351C_REVID 0x03 +#define SI5351C_CACHED_REGS 188 typedef enum { - SI5351C_PLL_A = 1, - SI5351C_PLL_B = 2, - SI5351C_PLL_BOTH = 3, + SI5351C_MODE_FRAC = 0, + SI5351C_MODE_INT = 1, +} si5351c_mode_t; + +typedef enum { + SI5351C_SRC_XTAL = 0, + SI5351C_SRC_CLKIN = 1, + SI5351C_SRC_MULTISYNTH_0_4 = 2, + SI5351C_SRC_MULTISYNTH_SELF = 3, +} si5351c_src_t; + +typedef enum { + SI5351C_DRIVE_2MA = 0, + SI5351C_DRIVE_4MA = 1, + SI5351C_DRIVE_6MA = 2, + SI5351C_DRIVE_8MA = 3, +} si5351c_drive_t; + +typedef enum { + SI5351C_PLL_A = 0, + SI5351C_PLL_B = 1, } si5351c_pll_t; -#define SI5351C_CLK_PLL_SRC(x) ((x & SI5351C_PLL_B) << 4) +typedef enum { + SI5351C_PLL_MASK_A = 1, + SI5351C_PLL_MASK_B = 2, + SI5351C_PLL_MASK_BOTH = 3, +} si5351c_pll_mask_t; -enum pll_sources { - PLL_SOURCE_UNINITIALIZED = -1, - PLL_SOURCE_XTAL = 0, - PLL_SOURCE_CLKIN = 1, -}; +typedef enum { + SI5351C_XTAL_6PF = 1, + SI5351C_XTAL_8PF = 2, + SI5351C_XTAL_10PF = 3, +} si5351c_xtal_t; + +typedef enum { + SI5351C_OUTPUT_ENABLE = 0, + SI5351C_OUTPUT_DISABLE = 1, +} si5351c_output_t; + +typedef enum { + SI5351C_DIV_1 = 0, + SI5351C_DIV_2 = 1, + SI5351C_DIV_4 = 2, + SI5351C_DIV_8 = 3, +} si5351c_div_t; + +typedef enum { + SI5351C_INPUT_XTAL = 0, + SI5351C_INPUT_CLKIN = 1, +} si5351c_input_t; typedef struct { i2c_bus_t* const bus; uint8_t i2c_address; + uint8_t regs[SI5351C_CACHED_REGS]; + uint32_t regs_dirty[(SI5351C_CACHED_REGS + 31) / 32]; } si5351c_driver_t; +typedef struct { + bool power_down; + si5351c_mode_t mode; + si5351c_pll_t pll; + si5351c_src_t source; + si5351c_drive_t drive; + bool invert; +} si5351c_clk_ctrl_t; + void si5351c_disable_all_outputs(si5351c_driver_t* const drv); void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv); void si5351c_power_down_all_clocks(si5351c_driver_t* const drv); void si5351c_set_crystal_configuration(si5351c_driver_t* const drv); void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv); -void si5351c_configure_pll_sources( - si5351c_driver_t* const drv, - const enum pll_sources source); +void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input); void si5351c_configure_pll_multisynth( si5351c_driver_t* const drv, - const enum pll_sources source); -void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll); + const si5351c_input_t input); +void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask); void si5351c_configure_multisynth( si5351c_driver_t* const drv, const uint_fast8_t ms_number, @@ -100,15 +128,11 @@ void si5351c_set_int_mode( si5351c_driver_t* const drv, const uint_fast8_t ms_number, const uint_fast8_t on); -void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source); +void si5351c_change_input(si5351c_driver_t* const drv, const si5351c_input_t input); bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv); void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val); uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg); -void si5351c_write( - si5351c_driver_t* const drv, - const uint8_t* const data, - const size_t data_count); void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable); void si5351c_init(si5351c_driver_t* const drv); void si5351c_set_phase( diff --git a/firmware/common/si5351c_regs.def b/firmware/common/si5351c_regs.def new file mode 100644 index 00000000..5c3a6774 --- /dev/null +++ b/firmware/common/si5351c_regs.def @@ -0,0 +1,494 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +static inline void si5351c_reg_set_clean(si5351c_driver_t* drv, int reg) +{ + drv->regs_dirty[reg / 32] &= ~(1 << (reg % 32)); +} + +static inline void si5351c_reg_set_dirty(si5351c_driver_t* drv, int reg) +{ + drv->regs_dirty[reg / 32] |= (1 << (reg % 32)); +} + +static inline bool si5351c_reg_is_dirty(si5351c_driver_t* drv, int reg) +{ + return (drv->regs_dirty[reg / 32] & (1 << (reg % 32))) ? true : false; +} + +/* Generate static inline accessors that operate on the global + * regs. Done this way to (1) allow defs to be scraped out and used + * elsewhere, e.g. in scripts, (2) to avoid dealing with endian + * (structs). This may be used in firmware, or on host predefined + * register loads. */ + +/* On set_, register is always set dirty, even if nothing + * changed. This makes sure that writes that have side effects, + * e.g. frequency setting, are not skipped. */ + +/* n=name, r=regnum, o=offset (bits from LSB) of LSB of field, + * l=length (bits) */ +#define __MREG__(n,r,o,l) \ +static inline uint8_t get_##n(si5351c_driver_t* const _d) { \ + return (_d->regs[r] >> o) & ((1L<regs[r] &= (uint8_t)(~(((1L<regs[r] |= (uint8_t)(((v&((1L<> 8); \ + set_##n##_BOT(_d, v); \ +} + +#define __3PART__(n) \ +static inline uint32_t get_##n(si5351c_driver_t* const _d) { \ + return (get_##n##_TOP(_d) << 16) | (get_##n##_MID(_d) << 8) | get_##n##_BOT(_d); \ +} \ +static inline void set_##n(si5351c_driver_t* const _d, uint32_t v) { \ + set_##n##_TOP(_d, v >> 16); \ + set_##n##_MID(_d, v >> 8); \ + set_##n##_BOT(_d, v); \ +} + +/* Generate accessors for per-PLL and per-multisynth registers */ +/* n=name, p=prefix, s=suffix */ + +#define __PER_PLL__(n, p, s) \ +static inline uint32_t get_##n(si5351c_driver_t* const _d, si5351c_pll_t pll) { \ + return (pll == SI5351C_PLL_A) ? get_##p##A##s(_d) : get_##p##B##s(_d); \ +} \ +static inline void set_##n(si5351c_driver_t* const _d, si5351c_pll_t pll, uint32_t v) { \ + if (pll == SI5351C_PLL_A) { \ + set_##p##A##s(_d, v); \ + } else { \ + set_##p##B##s(_d, v); \ + } \ +} + +#define __PER_MS__(n, p, s) \ +static inline uint32_t get_##n(si5351c_driver_t* const _d, uint8_t i) { \ + switch (i) { \ + case 0: return get_##p##0##s(_d); \ + case 1: return get_##p##1##s(_d); \ + case 2: return get_##p##2##s(_d); \ + case 3: return get_##p##3##s(_d); \ + case 4: return get_##p##4##s(_d); \ + case 5: return get_##p##5##s(_d); \ + case 6: return get_##p##6##s(_d); \ + case 7: return get_##p##7##s(_d); \ + default: return 0; \ + } \ +} \ +static inline void set_##n(si5351c_driver_t* const _d, uint8_t i, uint32_t v) { \ + switch (i) { \ + case 0: set_##p##0##s(_d, v); return; \ + case 1: set_##p##1##s(_d, v); return; \ + case 2: set_##p##2##s(_d, v); return; \ + case 3: set_##p##3##s(_d, v); return; \ + case 4: set_##p##4##s(_d, v); return; \ + case 5: set_##p##5##s(_d, v); return; \ + case 6: set_##p##6##s(_d, v); return; \ + case 7: set_##p##7##s(_d, v); return; \ + } \ +} \ +static inline void set_all_##n(si5351c_driver_t* const _d, uint32_t v) { \ + set_##p##0##s(_d, v); \ + set_##p##1##s(_d, v); \ + set_##p##2##s(_d, v); \ + set_##p##3##s(_d, v); \ + set_##p##4##s(_d, v); \ + set_##p##5##s(_d, v); \ + set_##p##6##s(_d, v); \ + set_##p##7##s(_d, v); \ +} + +#define __PER_FRAC_MS__(n, p, s) \ +static inline void set_##n(si5351c_driver_t* const _d, uint8_t i, uint32_t v) { \ + switch (i) { \ + case 0: set_##p##0##s(_d, v); return; \ + case 1: set_##p##1##s(_d, v); return; \ + case 2: set_##p##2##s(_d, v); return; \ + case 3: set_##p##3##s(_d, v); return; \ + case 4: set_##p##4##s(_d, v); return; \ + case 5: set_##p##5##s(_d, v); return; \ + } \ +} + +/* Register 0: Device Status */ +__MREG__(SYS_INIT, 0, 7, 1) +__MREG__(LOL_B, 0, 6, 1) +__MREG__(LOL_A, 0, 5, 1) +__MREG__(LOS_CLKIN, 0, 4, 1) +__MREG__(LOS_XTAL, 0, 3, 1) +__MREG__(REVID, 0, 0, 2) + +/* Register 1: Interrupt Status Sticky */ +__MREG__(SYS_INIT_STKY, 1, 7, 1) +__MREG__(LOL_B_STKY, 1, 6, 1) +__MREG__(LOL_A_STKY, 1, 5, 1) +__MREG__(LOS_CLKIN_STKY, 1, 4, 1) +__MREG__(LOS_XTAL_STKY, 1, 3, 1) + +/* Register 2: Interrupt Status Mask */ +__MREG__(SYS_INIT_MASK, 2, 7, 1) +__MREG__(LOL_B_MASK, 2, 6, 1) +__MREG__(LOL_A_MASK, 2, 5, 1) +__MREG__(LOS_CLKIN_MASK, 2, 4, 1) +__MREG__(LOS_XTAL_MASK, 2, 3, 1) + +/* Register 3: Output Enable Control */ +__MREG__(CLK7_OEB, 3, 7, 1) +__MREG__(CLK6_OEB, 3, 6, 1) +__MREG__(CLK5_OEB, 3, 5, 1) +__MREG__(CLK4_OEB, 3, 4, 1) +__MREG__(CLK3_OEB, 3, 3, 1) +__MREG__(CLK2_OEB, 3, 2, 1) +__MREG__(CLK1_OEB, 3, 1, 1) +__MREG__(CLK0_OEB, 3, 0, 1) + +/* Register 4: OEB Pin Enable Control */ +__MREG__(OEB_CLK7, 4, 7, 1) +__MREG__(OEB_CLK6, 4, 6, 1) +__MREG__(OEB_CLK5, 4, 5, 1) +__MREG__(OEB_CLK4, 4, 4, 1) +__MREG__(OEB_CLK3, 4, 3, 1) +__MREG__(OEB_CLK2, 4, 2, 1) +__MREG__(OEB_CLK1, 4, 1, 1) +__MREG__(OEB_CLK0, 4, 0, 1) + +/* Register 9: OEB Pin Enable Control Mask */ +__MREG__(OEB_MASK7, 9, 7, 1) +__MREG__(OEB_MASK6, 9, 6, 1) +__MREG__(OEB_MASK5, 9, 5, 1) +__MREG__(OEB_MASK4, 9, 4, 1) +__MREG__(OEB_MASK3, 9, 3, 1) +__MREG__(OEB_MASK2, 9, 2, 1) +__MREG__(OEB_MASK1, 9, 1, 1) +__MREG__(OEB_MASK0, 9, 0, 1) + +/* Register 15: PLL Input Source */ +__MREG__(CLKIN_DIV, 15, 6, 2) +__MREG__(PLLB_SRC, 15, 3, 1) +__MREG__(PLLA_SRC, 15, 2, 1) + +/* Register 16: CLK0 Control */ +__MREG__(CLK0_PDN, 16, 7, 1) +__MREG__(MS0_INT, 16, 6, 1) +__MREG__(MS0_SRC, 16, 5, 1) +__MREG__(CLK0_INV, 16, 4, 1) +__MREG__(CLK0_SRC, 16, 2, 2) +__MREG__(CLK0_IDRV, 16, 0, 2) + +/* Register 17: CLK1 Control */ +__MREG__(CLK1_PDN, 17, 7, 1) +__MREG__(MS1_INT, 17, 6, 1) +__MREG__(MS1_SRC, 17, 5, 1) +__MREG__(CLK1_INV, 17, 4, 1) +__MREG__(CLK1_SRC, 17, 2, 2) +__MREG__(CLK1_IDRV, 17, 0, 2) + +/* Register 18: CLK2 Control */ +__MREG__(CLK2_PDN, 18, 7, 1) +__MREG__(MS2_INT, 18, 6, 1) +__MREG__(MS2_SRC, 18, 5, 1) +__MREG__(CLK2_INV, 18, 4, 1) +__MREG__(CLK2_SRC, 18, 2, 2) +__MREG__(CLK2_IDRV, 18, 0, 2) + +/* Register 19: CLK3 Control */ +__MREG__(CLK3_PDN, 19, 7, 1) +__MREG__(MS3_INT, 19, 6, 1) +__MREG__(MS3_SRC, 19, 5, 1) +__MREG__(CLK3_INV, 19, 4, 1) +__MREG__(CLK3_SRC, 19, 2, 2) +__MREG__(CLK3_IDRV, 19, 0, 2) + +/* Register 20: CLK4 Control */ +__MREG__(CLK4_PDN, 20, 7, 1) +__MREG__(MS4_INT, 20, 6, 1) +__MREG__(MS4_SRC, 20, 5, 1) +__MREG__(CLK4_INV, 20, 4, 1) +__MREG__(CLK4_SRC, 20, 2, 2) +__MREG__(CLK4_IDRV, 20, 0, 2) + +/* Register 21: CLK5 Control */ +__MREG__(CLK5_PDN, 21, 7, 1) +__MREG__(MS5_INT, 21, 6, 1) +__MREG__(MS5_SRC, 21, 5, 1) +__MREG__(CLK5_INV, 21, 4, 1) +__MREG__(CLK5_SRC, 21, 2, 2) +__MREG__(CLK5_IDRV, 21, 0, 2) + +/* Register 22: CLK6 Control */ +__MREG__(CLK6_PDN, 22, 7, 1) +__MREG__(FBA_INT, 22, 6, 1) +__MREG__(MS6_SRC, 22, 5, 1) +__MREG__(CLK6_INV, 22, 4, 1) +__MREG__(CLK6_SRC, 22, 2, 2) +__MREG__(CLK6_IDRV, 22, 0, 2) + +/* Register 23: CLK7 Control */ +__MREG__(CLK7_PDN, 23, 7, 1) +__MREG__(FBB_INT, 23, 6, 1) +__MREG__(MS7_SRC, 23, 5, 1) +__MREG__(CLK7_INV, 23, 4, 1) +__MREG__(CLK7_SRC, 23, 2, 2) +__MREG__(CLK7_IDRV, 23, 0, 2) + +/* Register 24: CLK0-CLK3 Disable State */ +__MREG__(CLK3_DIS_STATE, 24, 6, 2) +__MREG__(CLK2_DIS_STATE, 24, 4, 2) +__MREG__(CLK1_DIS_STATE, 24, 2, 2) +__MREG__(CLK0_DIS_STATE, 24, 0, 2) + +/* Register 25: CLK4-CLK7 Disable State */ +__MREG__(CLK7_DIS_STATE, 25, 6, 2) +__MREG__(CLK6_DIS_STATE, 25, 4, 2) +__MREG__(CLK5_DIS_STATE, 25, 2, 2) +__MREG__(CLK4_DIS_STATE, 25, 0, 2) + +/* Registers 26-33: Multisynth NA Parameters */ +__MREG__(MSNA_P3_MID, 26, 0, 8) +__MREG__(MSNA_P3_BOT, 27, 0, 8) +__MREG__(MSNA_P1_TOP, 28, 0, 2) +__MREG__(MSNA_P1_MID, 29, 0, 8) +__MREG__(MSNA_P1_BOT, 30, 0, 8) +__MREG__(MSNA_P3_TOP, 31, 4, 4) +__MREG__(MSNA_P2_TOP, 31, 0, 4) +__MREG__(MSNA_P2_MID, 32, 0, 8) +__MREG__(MSNA_P2_BOT, 33, 0, 8) + +/* Registers 34-41: Multisynth NB Parameters */ +__MREG__(MSNB_P3_MID, 34, 0, 8) +__MREG__(MSNB_P3_BOT, 35, 0, 8) +__MREG__(MSNB_P1_TOP, 36, 0, 2) +__MREG__(MSNB_P1_MID, 37, 0, 8) +__MREG__(MSNB_P1_BOT, 38, 0, 8) +__MREG__(MSNB_P3_TOP, 39, 4, 4) +__MREG__(MSNB_P2_TOP, 39, 0, 4) +__MREG__(MSNB_P2_MID, 40, 0, 8) +__MREG__(MSNB_P2_BOT, 41, 0, 8) + +/* Registers 42-49: Multisynth 0 Parameters */ +__MREG__(MS0_P3_MID, 42, 0, 8) +__MREG__(MS0_P3_BOT, 43, 0, 8) +__MREG__(R0_DIV, 44, 4, 3) +__MREG__(MS0_DIVBY4, 44, 2, 2) +__MREG__(MS0_P1_TOP, 44, 0, 2) +__MREG__(MS0_P1_MID, 45, 0, 8) +__MREG__(MS0_P1_BOT, 46, 0, 8) +__MREG__(MS0_P3_TOP, 47, 4, 4) +__MREG__(MS0_P2_TOP, 47, 0, 4) +__MREG__(MS0_P2_MID, 48, 0, 8) +__MREG__(MS0_P2_BOT, 49, 0, 8) + +/* Registers 50-57: Multisynth 1 Parameters */ +__MREG__(MS1_P3_MID, 50, 0, 8) +__MREG__(MS1_P3_BOT, 51, 0, 8) +__MREG__(R1_DIV, 52, 4, 3) +__MREG__(MS1_DIVBY4, 52, 2, 2) +__MREG__(MS1_P1_TOP, 52, 0, 2) +__MREG__(MS1_P1_MID, 53, 0, 8) +__MREG__(MS1_P1_BOT, 54, 0, 8) +__MREG__(MS1_P3_TOP, 55, 4, 4) +__MREG__(MS1_P2_TOP, 55, 0, 4) +__MREG__(MS1_P2_MID, 56, 0, 8) +__MREG__(MS1_P2_BOT, 57, 0, 8) + +/* Registers 58-65: Multisynth 2 Parameters */ +__MREG__(MS2_P3_MID, 58, 0, 8) +__MREG__(MS2_P3_BOT, 59, 0, 8) +__MREG__(R2_DIV, 60, 4, 3) +__MREG__(MS2_DIVBY4, 60, 2, 2) +__MREG__(MS2_P1_TOP, 60, 0, 2) +__MREG__(MS2_P1_MID, 61, 0, 8) +__MREG__(MS2_P1_BOT, 62, 0, 8) +__MREG__(MS2_P3_TOP, 63, 4, 4) +__MREG__(MS2_P2_TOP, 63, 0, 4) +__MREG__(MS2_P2_MID, 64, 0, 8) +__MREG__(MS2_P2_BOT, 65, 0, 8) + +/* Registers 66-73: Multisynth 3 Parameters */ +__MREG__(MS3_P3_MID, 66, 0, 8) +__MREG__(MS3_P3_BOT, 67, 0, 8) +__MREG__(R3_DIV, 68, 4, 3) +__MREG__(MS3_DIVBY4, 68, 2, 2) +__MREG__(MS3_P1_TOP, 68, 0, 2) +__MREG__(MS3_P1_MID, 69, 0, 8) +__MREG__(MS3_P1_BOT, 70, 0, 8) +__MREG__(MS3_P3_TOP, 71, 4, 4) +__MREG__(MS3_P2_TOP, 71, 0, 4) +__MREG__(MS3_P2_MID, 72, 0, 8) +__MREG__(MS3_P2_BOT, 73, 0, 8) + +/* Registers 74-81: Multisynth 4 Parameters */ +__MREG__(MS4_P3_MID, 74, 0, 8) +__MREG__(MS4_P3_BOT, 75, 0, 8) +__MREG__(R4_DIV, 76, 4, 3) +__MREG__(MS4_DIVBY4, 76, 2, 2) +__MREG__(MS4_P1_TOP, 76, 0, 2) +__MREG__(MS4_P1_MID, 77, 0, 8) +__MREG__(MS4_P1_BOT, 78, 0, 8) +__MREG__(MS4_P3_TOP, 79, 4, 4) +__MREG__(MS4_P2_TOP, 79, 0, 4) +__MREG__(MS4_P2_MID, 80, 0, 8) +__MREG__(MS4_P2_BOT, 81, 0, 8) + +/* Registers 82-89: Multisynth 5 Parameters */ +__MREG__(MS5_P3_MID, 82, 0, 8) +__MREG__(MS5_P3_BOT, 83, 0, 8) +__MREG__(R5_DIV, 84, 4, 3) +__MREG__(MS5_DIVBY4, 84, 2, 2) +__MREG__(MS5_P1_TOP, 84, 0, 2) +__MREG__(MS5_P1_MID, 85, 0, 8) +__MREG__(MS5_P1_BOT, 86, 0, 8) +__MREG__(MS5_P3_TOP, 87, 4, 4) +__MREG__(MS5_P2_TOP, 87, 0, 4) +__MREG__(MS5_P2_MID, 88, 0, 8) +__MREG__(MS5_P2_BOT, 89, 0, 8) + +/* Register 90: Multisynth 6 Parameters */ +__MREG__(MS6_P1, 90, 0, 8) + +/* Register 91: Multisynth 7 Parameters */ +__MREG__(MS7_P1, 91, 0, 8) + +/* Register 92: Clock 6 and 7 Output Divider */ +__MREG__(R7_DIV, 92, 4, 3) +__MREG__(R6_DIV, 92, 0, 3) + +/* Registers 149-161: Spread Spectrum Parameters */ +__MREG__(SSC_EN, 149, 7, 1) +__MREG__(SSDN_P2_TOP, 149, 0, 7) +__MREG__(SSDN_P2_BOT, 150, 0, 8) +__MREG__(SSC_MODE, 151, 7, 1) +__MREG__(SSDN_P3_TOP, 151, 0, 7) +__MREG__(SSDN_P3_BOT, 152, 0, 8) +__MREG__(SSDN_P1_BOT, 153, 0, 8) +__MREG__(SSUDP_TOP, 154, 4, 4) +__MREG__(SSDN_P1_TOP, 154, 0, 4) +__MREG__(SSUDP_BOT, 155, 0, 8) +__MREG__(SSUP_P2_TOP, 156, 0, 7) +__MREG__(SSUP_P2_BOT, 157, 0, 8) +__MREG__(SSUP_P3_TOP, 158, 0, 8) +__MREG__(SSUP_P3_BOT, 159, 0, 8) +__MREG__(SSUP_P1_BOT, 160, 0, 8) +__MREG__(SS_NCLK, 161, 4, 4) +__MREG__(SSUP_P1_TOP, 161, 0, 4) + +/* Registers 162-163: VCXO Parameter */ +__MREG__(VCXO_PARAM_BOT, 162, 0, 8) +__MREG__(VCXO_PARAM_MID, 163, 0, 8) +__MREG__(VCXO_PARAM_TOP, 164, 0, 6) + +/* Registers 165-170: CLK0-CLK5 Initial Phase Offsets */ +__MREG__(CLK0_PHOFF, 165, 0, 7) +__MREG__(CLK1_PHOFF, 166, 0, 7) +__MREG__(CLK2_PHOFF, 167, 0, 7) +__MREG__(CLK3_PHOFF, 168, 0, 7) +__MREG__(CLK4_PHOFF, 169, 0, 7) +__MREG__(CLK5_PHOFF, 170, 0, 7) + +/* Register 177: PLL Reset */ +__MREG__(PLLB_RST, 177, 7, 1) +__MREG__(PLLA_RST, 177, 5, 1) + +/* Register 183: Crystal Internal Load Capacitance */ +__MREG__(XTAL_CL, 183, 6, 2) + +/* Register 187: Fanout Enable */ +__MREG__(CLKIN_FANOUT_EN, 187, 7, 1) +__MREG__(XO_FANOUT_EN, 187, 6, 1) +__MREG__(MS_FANOUT_EN, 187, 4, 1) + +/* 3-part parameters */ +__3PART__(MSNA_P1) +__3PART__(MSNA_P2) +__3PART__(MSNA_P3) +__3PART__(MSNB_P1) +__3PART__(MSNB_P2) +__3PART__(MSNB_P3) +__3PART__(MS0_P1) +__3PART__(MS0_P2) +__3PART__(MS0_P3) +__3PART__(MS1_P1) +__3PART__(MS1_P2) +__3PART__(MS1_P3) +__3PART__(MS2_P1) +__3PART__(MS2_P2) +__3PART__(MS2_P3) +__3PART__(MS3_P1) +__3PART__(MS3_P2) +__3PART__(MS3_P3) +__3PART__(MS4_P1) +__3PART__(MS4_P2) +__3PART__(MS4_P3) +__3PART__(MS5_P1) +__3PART__(MS5_P2) +__3PART__(MS5_P3) + +/* 2-part parameters */ +__2PART__(SSDN_P1) +__2PART__(SSDN_P2) +__2PART__(SSDN_P3) +__2PART__(SSUP_P1) +__2PART__(SSUP_P2) +__2PART__(SSUP_P3) +__2PART__(SSUDP) + +/* Per-PLL register groups */ +__PER_PLL__(LOL, LOL_, ) +__PER_PLL__(LOL_STKY, LOL_, _STKY) +__PER_PLL__(LOL_MASK, LOL_, _MASK) +__PER_PLL__(PLL_SRC, PLL, _SRC) +__PER_PLL__(MSN_P1, MSN, _P1) +__PER_PLL__(MSN_P2, MSN, _P2) +__PER_PLL__(MSN_P3, MSN, _P3) +__PER_PLL__(PLL_RST, PLL, _RST) + +/* Per-multisynth register groups */ +__PER_MS__(CLK_OEB, CLK, _OEB) +__PER_MS__(OEB_CLK, OEB_CLK, ) +__PER_MS__(OEB_MASK, OEB_MASK, ) +__PER_MS__(CLK_PDN, CLK, _PDN) +__PER_MS__(MS_SRC, MS, _SRC) +__PER_MS__(CLK_INV, CLK, _INV) +__PER_MS__(CLK_SRC, CLK, _SRC) +__PER_MS__(CLK_IDRV, CLK, _IDRV) +__PER_MS__(CLK_DIS_STATE, CLK, _DIS_STATE) +__PER_MS__(R_DIV, R, _DIV) +__PER_MS__(MS_P1, MS, _P1) +__PER_FRAC_MS__(MS_INT, MS, _INT) +__PER_FRAC_MS__(MS_P2, MS, _P2) +__PER_FRAC_MS__(MS_P3, MS, _P3) +__PER_FRAC_MS__(MS_DIVBY4, MS, _DIVBY4) +__PER_FRAC_MS__(CLK_PHOFF, CLK, _PHOFF)