[PATCH] clk: add si5351 i2c common clock driver
From: zonque@gmail.com (Daniel Mack)
Date: 2013-02-19 19:15:51
Also in:
linux-devicetree, lkml
Hi Sebastian, I did some more tests today and it took me a while to dig for the root cause why things were not working for me in the first place - see below. On 09.02.2013 13:59, Sebastian Hesselbarth wrote:
+==Example==
+
+/* 25MHz reference crystal */
+ref25: ref25M {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+};
+
+i2c-master-node {
+
+ /* Si5351a msop10 i2c clock generator */
+ si5351a: clock-generator at 60 {
+ compatible = "silabs,si5351a-msop";
+ reg = <0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #clock-cells = <1>;
+
+ /* connect xtal input to 25MHz reference */
+ clocks = <&ref25>;As referred to in another thread, registering the ref25M clock that way didn't suffice for me on my platform - but that's a different story.
+static void si5351_read_parameters(struct si5351_driver_data *drvdata,
+ unsigned char reg, struct si5351_parameters *params)
+{
+ unsigned char buf[SI5351_PARAMETERS_LENGTH];On a general note, I think you can use u8 instead of unsigned char all over the place here, which will save you some indentation trouble.
+static inline int _si5351_clkout_reparent(struct si5351_driver_data *drvdata,
+ unsigned char num, unsigned char parent)
+{
+ struct clk *pclk;
+
+ if (num > 8 ||
+ (drvdata->variant == SI5351_VARIANT_A3 && num > 3))
+ return -EINVAL;
+
+ switch (parent) {
+ case 0:
+ pclk = drvdata->msynth[num].hw.clk;
+ break;
+ case 1:
+ pclk = drvdata->msynth[0].hw.clk;
+ if (num >= 4)
+ pclk = drvdata->msynth[4].hw.clk;
+ break;
+ case 2:
+ pclk = drvdata->xtal.clk;
+ break;
+ case 3:
+ if (drvdata->variant != SI5351_VARIANT_C)
+ return -EINVAL;
+ pclk = drvdata->clkin.clk;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return clk_set_parent(drvdata->clkout[num].hw.clk, pclk);
+}[...]
+static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned val;
+
+ val = 0;
+ hw->clk->flags &= ~CLK_SET_RATE_PARENT;
+ switch (index) {
+ case 0:
+ hw->clk->flags |= CLK_SET_RATE_PARENT;
+ val = SI5351_CLK_INPUT_MULTISYNTH_N;
+ break;I fugured that _si5351_clkout_reparent() wouldn't actually call ->set_parent() on the clock, which leads to the fact that CLK_SET_RATE_PARENT is not set in the flags. That way, only the clkout end leaf is actually supplied with a new rate, which leads to incorrect effective clocks, depending on the current multisynth/pll configuration. The reason for this is in clk_set_parent() itself, which bails if the parent is already set to the passed value: if (clk->parent == parent) goto out; I fixed that for now by explicitly setting the clock's parent to NULL before calling clk_set_parent() in _si5351_clkout_reparent(), so the calbacks are triggered. But there might be a nicer way, for example to factor out the CLK_SET_RATE_PARENT handling to some function called from _si5351_clkout_reparent() or so. Anyway, with this hack in place along with the other details I mentioned in my first mail, the driver seems to work for me now, which is great. I will do more extensive tests later that week when I have access to better scopes ... Many thanks again, Daniel
quoted hunk ↗ jump to hunk
+ case 1: + /* clk0/clk4 can only connect to its own multisync */ + if (hwdata->num == 0 || hwdata->num == 4) + val = SI5351_CLK_INPUT_MULTISYNTH_N; + else + val = SI5351_CLK_INPUT_MULTISYNTH_0_4; + break; + case 2: + val = SI5351_CLK_INPUT_XTAL; + break; + case 3: + val = SI5351_CLK_INPUT_CLKIN; + break; + } + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_INPUT_MASK, val); + + return 0; +} + +static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned char reg = SI5351_CLK0_PARAMETERS + + (SI5351_PARAMETERS_LENGTH * hwdata->num); + unsigned char rdiv; + + rdiv = (si5351_reg_read(hwdata->drvdata, reg + 2) & + SI5351_OUTPUT_CLK_DIV_MASK) >> SI5351_OUTPUT_CLK_DIV_SHIFT; + + return parent_rate >> rdiv; +} + +static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned char rdiv; + + /* clkout6/7 can only handle output freqencies < 150MHz */ + if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ) + rate = SI5351_CLKOUT67_MAX_FREQ; + + /* clkout freqency is 8kHz - 160MHz */ + if (rate > SI5351_CLKOUT_MAX_FREQ) + rate = SI5351_CLKOUT_MAX_FREQ; + if (rate < SI5351_CLKOUT_MIN_FREQ) + rate = SI5351_CLKOUT_MIN_FREQ; + + /* request frequency if multisync master */ + if (hwdata->hw.clk->flags & CLK_SET_RATE_PARENT) { + /* use r divider for frequencies below 1MHz */ + rdiv = SI5351_OUTPUT_CLK_DIV_1; + while (rate < SI5351_MULTISYNTH_MIN_FREQ && + rdiv < SI5351_OUTPUT_CLK_DIV_128) { + rdiv += 1; + rate *= 2; + } + *parent_rate = rate; + } else { + unsigned long new_rate, new_err, err; + + /* round to closed rdiv */ + rdiv = SI5351_OUTPUT_CLK_DIV_1; + new_rate = *parent_rate; + err = abs(new_rate - rate); + do { + new_rate >>= 1; + new_err = abs(new_rate - rate); + if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128) + break; + rdiv++; + err = new_err; + } while (1); + } + rate = *parent_rate >> rdiv; + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", + __func__, hwdata->hw.clk->name, (1 << rdiv), *parent_rate, + rate); + + return rate; +} + +static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned char reg = SI5351_CLK0_PARAMETERS + + (SI5351_PARAMETERS_LENGTH * hwdata->num); + unsigned long new_rate, new_err, err; + unsigned char rdiv; + + /* round to closed rdiv */ + rdiv = SI5351_OUTPUT_CLK_DIV_1; + new_rate = parent_rate; + err = abs(new_rate - rate); + do { + new_rate >>= 1; + new_err = abs(new_rate - rate); + if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128) + break; + rdiv++; + err = new_err; + } while (1); + + /* powerdown clkout */ + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN); + + /* write output divider */ + switch (hwdata->num) { + case 6: + si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER, + SI5351_OUTPUT_CLK6_DIV_MASK, rdiv); + break; + case 7: + si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER, + SI5351_OUTPUT_CLK_DIV_MASK, + rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT); + break; + default: + si5351_set_bits(hwdata->drvdata, reg + 2, + SI5351_OUTPUT_CLK_DIV_MASK, + rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT); + } + + /* powerup clkout */ + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_POWERDOWN, 0); + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", + __func__, hwdata->hw.clk->name, (1 << rdiv), parent_rate, rate); + + return 0; +} + +static const struct clk_ops si5351_clkout_ops = { + .prepare = si5351_clkout_prepare, + .unprepare = si5351_clkout_unprepare, + .is_enabled = si5351_clkout_is_enabled, + .set_parent = si5351_clkout_set_parent, + .get_parent = si5351_clkout_get_parent, + .recalc_rate = si5351_clkout_recalc_rate, + .round_rate = si5351_clkout_round_rate, + .set_rate = si5351_clkout_set_rate, +}; + +/* + * Si5351 i2c probe and DT + */ +static void si5351_dt_setup( + struct i2c_client *client, struct si5351_driver_data *drvdata) +{ + struct device_node *np = client->dev.of_node; + struct property *prop; + const __be32 *p; + unsigned int num, val; + + if (np == NULL) + return; + + /* + * property pll-source : <num src>, [<..>] + * allow to selectively set pll source + */ + of_property_for_each_u32(client->dev.of_node, "pll-source", + prop, p, num) { + if (num >= 2) { + dev_err(&client->dev, + "invalid pll %d on pll-source prop\n", num); + break; + } + + p = of_prop_next_u32(prop, p, &val); + if (!p) + break; + + if (_si5351_pll_reparent(drvdata, num, val)) + dev_warn(&client->dev, + "unable to reparent pll %d to %d\n", + num, val); + } + + for_each_child_of_node(client->dev.of_node, np) { + if (of_property_read_u32(np, "reg", &num)) { + dev_err(&client->dev, "missing reg property of %s\n", + np->full_name); + continue; + } + + if (of_property_read_bool(np, "pll-master")) + _si5351_msynth_set_pll_master(drvdata, num, 1); + + if (!of_property_read_u32(np, "drive-strength", &val)) { + if (_si5351_clkout_set_drive_strength(drvdata, + num, val)) + dev_warn(&client->dev, + "unable to set drive strength of %d to %d\n", + num, val); + } + + if (!of_property_read_u32(np, "multisynth-source", &val)) { + if (_si5351_msynth_reparent(drvdata, num, val)) + dev_warn(&client->dev, + "unable to reparent multisynth %d to %d\n", + num, val); + } + + if (!of_property_read_u32(np, "clock-source", &val)) { + if (_si5351_clkout_reparent(drvdata, num, val)) + dev_warn(&client->dev, + "unable to reparent clockout %d to %d\n", + num, val); + } + + if (!of_property_read_u32(np, "clock-frequency", &val)) + clk_set_rate(drvdata->onecell.clks[num], val); + } +} + +static const struct of_device_id si5351_dt_ids[] = { + { .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, }, + { .compatible = "silabs,si5351a-msop", + .data = (void *)SI5351_VARIANT_A3, }, + { .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, }, + { .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si5351_dt_ids); + +static int si5351_dt_parse( + struct i2c_client *client, struct si5351_driver_data *drvdata) +{ + struct device_node *np = client->dev.of_node; + const struct of_device_id *match; + + if (np == NULL) + return -EINVAL; + + match = of_match_node(si5351_dt_ids, np); + if (match == NULL) + return -EINVAL; + + drvdata->variant = (enum si5351_variant)match->data; + drvdata->pxtal = of_clk_get(np, 0); + drvdata->pclkin = of_clk_get(np, 1); + + return 0; +} + +static int si5351_i2c_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + struct si5351_driver_data *drvdata; + struct clk_init_data init; + struct clk *clk; + const char *parent_names[4]; + u8 num_parents, num_clocks; + int ret, n; + + drvdata = devm_kzalloc(&client->dev, sizeof(struct si5351_driver_data), + GFP_KERNEL); + if (drvdata == NULL) { + dev_err(&client->dev, "unable to allocate driver data\n"); + return -ENOMEM; + } + + ret = si5351_dt_parse(client, drvdata); + if (ret) + return ret; + + i2c_set_clientdata(client, drvdata); + drvdata->client = client; + drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config); + if (IS_ERR(drvdata->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(drvdata->regmap); + } + + /* Disable interrupts */ + si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0); + /* Set disabled output drivers to drive low */ + si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00); + si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00); + /* Ensure pll select is on XTAL for Si5351A/B */ + if (drvdata->variant != SI5351_VARIANT_C) + si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, + SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0); + + /* register xtal input clock gate */ + memset(&init, 0, sizeof(struct clk_init_data)); + init.name = si5351_input_names[0]; + init.ops = &si5351_xtal_ops; + init.flags = 0; + if (!IS_ERR(drvdata->pxtal)) { + init.parent_names = &drvdata->pxtal->name; + init.num_parents = 1; + } + drvdata->xtal.init = &init; + clk = devm_clk_register(&client->dev, &drvdata->xtal); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + return PTR_ERR(clk); + } + + /* register clkin input clock gate */ + if (drvdata->variant == SI5351_VARIANT_C) { + memset(&init, 0, sizeof(struct clk_init_data)); + init.name = si5351_input_names[1]; + init.ops = &si5351_clkin_ops; + if (!IS_ERR(drvdata->pclkin)) { + init.parent_names = &drvdata->pclkin->name; + init.num_parents = 1; + } + drvdata->clkin.init = &init; + clk = devm_clk_register(&client->dev, &drvdata->clkin); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + return PTR_ERR(clk); + } + } + + /* Si5351C allows to mux either xtal or clkin to PLL input */ + num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1; + parent_names[0] = si5351_input_names[0]; + parent_names[1] = si5351_input_names[1]; + + /* register PLLA */ + drvdata->pll[0].num = 0; + drvdata->pll[0].drvdata = drvdata; + drvdata->pll[0].hw.init = &init; + memset(&init, 0, sizeof(struct clk_init_data)); + init.name = si5351_pll_names[0]; + init.ops = &si5351_pll_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = num_parents; + clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + return -EINVAL; + } + + /* register PLLB or VXCO (Si5351B) */ + drvdata->pll[1].num = 1; + drvdata->pll[1].drvdata = drvdata; + drvdata->pll[1].hw.init = &init; + memset(&init, 0, sizeof(struct clk_init_data)); + if (drvdata->variant == SI5351_VARIANT_B) { + init.name = si5351_pll_names[2]; + init.ops = &si5351_vxco_ops; + init.flags = CLK_IS_ROOT; + init.parent_names = NULL; + init.num_parents = 0; + } else { + init.name = si5351_pll_names[1]; + init.ops = &si5351_pll_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = num_parents; + } + clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + return -EINVAL; + } + + /* register clk multisync and clk out divider */ + num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8; + parent_names[0] = si5351_pll_names[0]; + if (drvdata->variant == SI5351_VARIANT_B) + parent_names[1] = si5351_pll_names[2]; + else + parent_names[1] = si5351_pll_names[1]; + + drvdata->msynth = devm_kzalloc(&client->dev, + num_clocks * sizeof(struct si5351_hw_data), GFP_KERNEL); + + drvdata->clkout = devm_kzalloc(&client->dev, + num_clocks * sizeof(struct si5351_hw_data), GFP_KERNEL); + + drvdata->onecell.clk_num = num_clocks; + drvdata->onecell.clks = devm_kzalloc(&client->dev, + num_clocks * sizeof(struct clk *), GFP_KERNEL); + + if (WARN_ON(!drvdata->msynth || !drvdata->clkout || + !drvdata->onecell.clks)) + return -ENOMEM; + + for (n = 0; n < num_clocks; n++) { + drvdata->msynth[n].num = n; + drvdata->msynth[n].drvdata = drvdata; + drvdata->msynth[n].hw.init = &init; + memset(&init, 0, sizeof(struct clk_init_data)); + init.name = si5351_msynth_names[n]; + init.ops = &si5351_msynth_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = 2; + clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + return -EINVAL; + } + } + + num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 4 : 3; + parent_names[2] = si5351_input_names[0]; + parent_names[3] = si5351_input_names[1]; + for (n = 0; n < num_clocks; n++) { + parent_names[0] = si5351_msynth_names[n]; + parent_names[1] = (n < 4) ? si5351_msynth_names[0] : + si5351_msynth_names[4]; + + drvdata->clkout[n].num = n; + drvdata->clkout[n].drvdata = drvdata; + drvdata->clkout[n].hw.init = &init; + memset(&init, 0, sizeof(struct clk_init_data)); + init.name = si5351_clkout_names[n]; + init.ops = &si5351_clkout_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = num_parents; + clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + return -EINVAL; + } + drvdata->onecell.clks[n] = clk; + } + + /* setup clock setup from DT */ + si5351_dt_setup(client, drvdata); + + of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, + &drvdata->onecell); + + dev_info(&client->dev, "registered si5351 i2c client\n"); + + return 0; +} + +static int si5351_i2c_remove(struct i2c_client *client) +{ + i2c_set_clientdata(client, NULL); + return 0; +} + +static const struct i2c_device_id si5351_i2c_ids[] = { + { "silabs,si5351", SI5351_BUS_BASE_ADDR | 0 }, + { "silabs,si5351", SI5351_BUS_BASE_ADDR | 1 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids); + +static struct i2c_driver si5351_driver = { + .driver = { + .name = "si5351", + .of_match_table = si5351_dt_ids, + }, + .probe = si5351_i2c_probe, + .remove = si5351_i2c_remove, + .id_table = si5351_i2c_ids, +}; + +static int __init si5351_module_init(void) +{ + return i2c_add_driver(&si5351_driver); +} +module_init(si5351_module_init); + +static void __exit si5351_module_exit(void) +{ + i2c_del_driver(&si5351_driver); +} +module_exit(si5351_module_exit); + +MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.de"); +MODULE_DESCRIPTION("Silicon Labs Si5351A/B/C clock generator driver"); +MODULE_LICENSE("GPL");diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h new file mode 100644 index 0000000..424073c --- /dev/null +++ b/drivers/clk/clk-si5351.h@@ -0,0 +1,155 @@ +/* + * clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator + * + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Rabeeh Khoury <rabeeh@solid-run.com> + * + * 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 of the License, or (at your + * option) any later version. + */ + +#ifndef _CLK_SI5351_H_ +#define _CLK_SI5351_H_ + +#define SI5351_BUS_BASE_ADDR 0x60 + +#define SI5351_PLL_VCO_MIN 600000000 +#define SI5351_PLL_VCO_MAX 900000000 +#define SI5351_MULTISYNTH_MIN_FREQ 1000000 +#define SI5351_MULTISYNTH_DIVBY4_FREQ 150000000 +#define SI5351_MULTISYNTH_MAX_FREQ 160000000 +#define SI5351_MULTISYNTH67_MAX_FREQ SI5351_MULTISYNTH_DIVBY4_FREQ +#define SI5351_CLKOUT_MIN_FREQ 8000 +#define SI5351_CLKOUT_MAX_FREQ SI5351_MULTISYNTH_MAX_FREQ +#define SI5351_CLKOUT67_MAX_FREQ SI5351_MULTISYNTH67_MAX_FREQ + +#define SI5351_PLL_A_MIN 15 +#define SI5351_PLL_A_MAX 90 +#define SI5351_PLL_B_MAX (SI5351_PLL_C_MAX-1) +#define SI5351_PLL_C_MAX 1048575 +#define SI5351_MULTISYNTH_A_MIN 6 +#define SI5351_MULTISYNTH_A_MAX 1800 +#define SI5351_MULTISYNTH67_A_MAX 254 +#define SI5351_MULTISYNTH_B_MAX (SI5351_MULTISYNTH_C_MAX-1) +#define SI5351_MULTISYNTH_C_MAX 1048575 +#define SI5351_MULTISYNTH_P1_MAX ((1<<18)-1) +#define SI5351_MULTISYNTH_P2_MAX ((1<<20)-1) +#define SI5351_MULTISYNTH_P3_MAX ((1<<20)-1) + +#define SI5351_DEVICE_STATUS 0 +#define SI5351_INTERRUPT_STATUS 1 +#define SI5351_INTERRUPT_MASK 2 +#define SI5351_STATUS_SYS_INIT (1<<7) +#define SI5351_STATUS_LOL_B (1<<6) +#define SI5351_STATUS_LOL_A (1<<5) +#define SI5351_STATUS_LOS (1<<4) +#define SI5351_OUTPUT_ENABLE_CTRL 3 +#define SI5351_OEB_PIN_ENABLE_CTRL 9 +#define SI5351_PLL_INPUT_SOURCE 15 +#define SI5351_CLKIN_DIV_MASK (3<<6) +#define SI5351_CLKIN_DIV_1 (0<<6) +#define SI5351_CLKIN_DIV_2 (1<<6) +#define SI5351_CLKIN_DIV_4 (2<<6) +#define SI5351_CLKIN_DIV_8 (3<<6) +#define SI5351_PLLB_SOURCE (1<<3) +#define SI5351_PLLA_SOURCE (1<<2) + +#define SI5351_CLK0_CTRL 16 +#define SI5351_CLK1_CTRL 17 +#define SI5351_CLK2_CTRL 18 +#define SI5351_CLK3_CTRL 19 +#define SI5351_CLK4_CTRL 20 +#define SI5351_CLK5_CTRL 21 +#define SI5351_CLK6_CTRL 22 +#define SI5351_CLK7_CTRL 23 +#define SI5351_CLK_POWERDOWN (1<<7) +#define SI5351_CLK_INTEGER_MODE (1<<6) +#define SI5351_CLK_PLL_SELECT (1<<5) +#define SI5351_CLK_INVERT (1<<4) +#define SI5351_CLK_INPUT_MASK (3<<2) +#define SI5351_CLK_INPUT_XTAL (0<<2) +#define SI5351_CLK_INPUT_CLKIN (1<<2) +#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2) +#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2) +#define SI5351_CLK_DRIVE_MASK (3<<0) +#define SI5351_CLK_DRIVE_2MA (0<<0) +#define SI5351_CLK_DRIVE_4MA (1<<0) +#define SI5351_CLK_DRIVE_6MA (2<<0) +#define SI5351_CLK_DRIVE_8MA (3<<0) + +#define SI5351_CLK3_0_DISABLE_STATE 24 +#define SI5351_CLK7_4_DISABLE_STATE 25 +#define SI5351_CLK_DISABLE_STATE_LOW 0 +#define SI5351_CLK_DISABLE_STATE_HIGH 1 +#define SI5351_CLK_DISABLE_STATE_FLOAT 2 +#define SI5351_CLK_DISABLE_STATE_NEVER 3 + +#define SI5351_PARAMETERS_LENGTH 8 +#define SI5351_PLLA_PARAMETERS 26 +#define SI5351_PLLB_PARAMETERS 34 +#define SI5351_CLK0_PARAMETERS 42 +#define SI5351_CLK1_PARAMETERS 50 +#define SI5351_CLK2_PARAMETERS 58 +#define SI5351_CLK3_PARAMETERS 66 +#define SI5351_CLK4_PARAMETERS 74 +#define SI5351_CLK5_PARAMETERS 82 +#define SI5351_CLK6_PARAMETERS 90 +#define SI5351_CLK7_PARAMETERS 91 +#define SI5351_CLK6_7_OUTPUT_DIVIDER 92 +#define SI5351_OUTPUT_CLK_DIV_MASK (7 << 4) +#define SI5351_OUTPUT_CLK6_DIV_MASK (7 << 0) +#define SI5351_OUTPUT_CLK_DIV_SHIFT 4 +#define SI5351_OUTPUT_CLK_DIV6_SHIFT 0 +#define SI5351_OUTPUT_CLK_DIV_1 0 +#define SI5351_OUTPUT_CLK_DIV_2 1 +#define SI5351_OUTPUT_CLK_DIV_4 2 +#define SI5351_OUTPUT_CLK_DIV_8 3 +#define SI5351_OUTPUT_CLK_DIV_16 4 +#define SI5351_OUTPUT_CLK_DIV_32 5 +#define SI5351_OUTPUT_CLK_DIV_64 6 +#define SI5351_OUTPUT_CLK_DIV_128 7 +#define SI5351_OUTPUT_CLK_DIVBY4 (3<<2) + +#define SI5351_SSC_PARAM0 149 +#define SI5351_SSC_PARAM1 150 +#define SI5351_SSC_PARAM2 151 +#define SI5351_SSC_PARAM3 152 +#define SI5351_SSC_PARAM4 153 +#define SI5351_SSC_PARAM5 154 +#define SI5351_SSC_PARAM6 155 +#define SI5351_SSC_PARAM7 156 +#define SI5351_SSC_PARAM8 157 +#define SI5351_SSC_PARAM9 158 +#define SI5351_SSC_PARAM10 159 +#define SI5351_SSC_PARAM11 160 +#define SI5351_SSC_PARAM12 161 + +#define SI5351_VXCO_PARAMETERS_LOW 162 +#define SI5351_VXCO_PARAMETERS_MID 163 +#define SI5351_VXCO_PARAMETERS_HIGH 164 + +#define SI5351_CLK0_PHASE_OFFSET 165 +#define SI5351_CLK1_PHASE_OFFSET 166 +#define SI5351_CLK2_PHASE_OFFSET 167 +#define SI5351_CLK3_PHASE_OFFSET 168 +#define SI5351_CLK4_PHASE_OFFSET 169 +#define SI5351_CLK5_PHASE_OFFSET 170 + +#define SI5351_PLL_RESET 177 +#define SI5351_PLL_RESET_B (1<<7) +#define SI5351_PLL_RESET_A (1<<5) + +#define SI5351_CRYSTAL_LOAD 183 +#define SI5351_CRYSTAL_LOAD_MASK (3<<6) +#define SI5351_CRYSTAL_LOAD_6PF (1<<6) +#define SI5351_CRYSTAL_LOAD_8PF (2<<6) +#define SI5351_CRYSTAL_LOAD_10PF (3<<6) + +#define SI5351_FANOUT_ENABLE 187 +#define SI5351_CLKIN_ENABLE (1<<7) +#define SI5351_XTAL_ENABLE (1<<6) +#define SI5351_MULTISYNTH_ENABLE (1<<4) + +#endif