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.hesselba...@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.hesselba...@gmail.com>
> + * Rabeeh Khoury <rab...@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
> 

_______________________________________________
devicetree-discuss mailing list
devicetree-discuss@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/devicetree-discuss

Reply via email to