Re: [PATCH] clk: add si5351 i2c common clock driver

From: Daniel Mack
Date: Tue Feb 19 2013 - 14:15:55 EST


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@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



> + 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@xxxxxxxx");
> +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@xxxxxxxxx>
> + * Rabeeh Khoury <rabeeh@xxxxxxxxxxxxx>
> + *
> + * 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
>

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/