Re: [RESEND PATCH v5 2/2] clk: cs2600: Add Fractional-N clock driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Quoting Paul Handrigan (2024-12-31 12:20:18)
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 713573b6c86c..6b279ebf9c80 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -209,6 +209,15 @@ config COMMON_CLK_CS2000_CP
>         help
>           If you say yes here you get support for the CS2000 clock multiplier.
>  
> +config COMMON_CLK_CS2600
> +       tristate "Clock driver for CS2600 Fractional-N Clock Synthesizer & Clock Multiplier"
> +       depends on I2C
> +       depends on OF

Drop this as I don't see how it has a build time dependency.

> +       select REGMAP_I2C
> +       help
> +         If you say yes here you get support for the CS2600 clock synthesizer
> +         and multiplier.
> +
>  config COMMON_CLK_EN7523
>         bool "Clock driver for Airoha EN7523 SoC system clocks"
>         depends on OF
> diff --git a/drivers/clk/clk-cs2600.c b/drivers/clk/clk-cs2600.c
> new file mode 100644
> index 000000000000..3623ba67f87c
> --- /dev/null
> +++ b/drivers/clk/clk-cs2600.c
> @@ -0,0 +1,1177 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// CS2600  --  CIRRUS LOGIC Fractional-N Clock Synthesizer & Clock Multiplier
> +//
> +// Copyright (C) 2024 Cirrus Logic, Inc. and
> +//                    Cirrus Logic International Semiconductor Ltd.
> +
> +#include <linux/clk.h>

This shouldn't be included as this is a clk provider, not a consumer.

> +#include <linux/clk-provider.h>
> +#include <linux/container_of.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regmap.h>
> +
> +#include "clk-cs2600.h"

Please inline the contents of this header into the one driver.

> +
> +#define hw_to_cs2600_clk(_hw)  container_of(_hw, struct cs2600_clk_hw, hw)
> +
[...]
> +
> +
> +static int cs2600_pll_out_is_prepared(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +
> +       return regmap_test_bits(regmap, CS2600_PLL_CFG1, CS2600_PLL_EN1);
> +}
> +
> +

Nitpick: Remove extra newline.

> +static int cs2600_pll_out_prepare(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +       int i;
> +
> +       if (regmap_test_bits(regmap, CS2600_PLL_CFG1, CS2600_PLL_EN1))
> +               return 0;
> +
> +       cs2600_set_freeze(cs2600);
> +       regmap_set_bits(regmap, CS2600_PLL_CFG1, CS2600_PLL_EN1);
> +       cs2600_clear_freeze(cs2600);
> +       regmap_set_bits(regmap, CS2600_PLL_CFG2, CS2600_PLL_EN2);
> +
> +       regmap_write(regmap, CS2600_UNLOCK_INDICATORS, CS2600_CLEAR_INDICATORS);
> +
> +       usleep_range(1000, 1100);
> +       for (i = 0; i < CS2600_LOCK_ATTEMPTS_MAX; i++) {
> +               if (!regmap_test_bits(regmap, CS2600_UNLOCK_INDICATORS,
> +                                     CS2600_F_UNLOCK_STICKY))
> +                       return 0;
> +
> +               regmap_write(regmap, CS2600_UNLOCK_INDICATORS,
> +                            CS2600_CLEAR_INDICATORS);
> +               usleep_range(200, 300);
> +       }
> +
> +       cs2600_set_freeze(cs2600);
> +       regmap_clear_bits(regmap, CS2600_PLL_CFG1, CS2600_PLL_EN1);
> +       cs2600_clear_freeze(cs2600);
> +       regmap_clear_bits(regmap, CS2600_PLL_CFG2, CS2600_PLL_EN2);
> +
> +       dev_err(cs2600->dev, "PLL did not lock\n");
> +       return -ETIMEDOUT;
> +}
> +
> +

Nitpick: Remove extra newline.

> +static void cs2600_pll_out_unprepare(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +
> +       cs2600_set_freeze(cs2600);
> +       regmap_clear_bits(regmap, CS2600_PLL_CFG1, CS2600_PLL_EN1);
> +       cs2600_clear_freeze(cs2600);
> +       regmap_clear_bits(regmap, CS2600_PLL_CFG2, CS2600_PLL_EN2);
> +}
> +
> +static int cs2600_pll_out_set_parent(struct clk_hw *hw, u8 index)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +
> +       /*CLK_IN is always the parent in smart mode */
> +       if (cs2600_is_smart_mode(cs2600))
> +               return 0;
> +
> +       if (index > CS2600_CLK_IN_INPUT) {
> +               dev_err(cs2600->dev, "Invalid parent index %d", index);
> +               return -EINVAL;
> +       }
> +
> +       cs2600_set_freeze(cs2600);
> +       regmap_update_bits(regmap, CS2600_PLL_CFG2,
> +                          CS2600_PLL_MODE_SEL, index);
> +       cs2600_clear_freeze(cs2600);
> +
> +       return 0;
> +}
> +
> +static u8 cs2600_pll_out_get_parent(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +
> +       if (cs2600_is_smart_mode(cs2600))
> +               return CS2600_CLK_IN_INPUT;
> +
> +       return (u8)regmap_test_bits(regmap, CS2600_PLL_CFG2,
> +                                   CS2600_PLL_MODE_SEL);
> +}
> +
> +static int cs2600_clk_out_prepare(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +
> +       cs2600_set_freeze(cs2600);
> +       regmap_clear_bits(regmap, CS2600_PLL_CFG1, CS2600_CLK_OUT_DIS);
> +       cs2600_clear_freeze(cs2600);
> +
> +       return 0;
> +}
> +
> +static void cs2600_clk_out_unprepare(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +
> +       cs2600_set_freeze(cs2600);
> +       regmap_set_bits(regmap, CS2600_PLL_CFG1, CS2600_CLK_OUT_DIS);
> +       cs2600_clear_freeze(cs2600);
> +}
> +
> +static int cs2600_clk_out_is_prepared(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +
> +       return !regmap_test_bits(regmap, CS2600_PLL_CFG1, CS2600_CLK_OUT_DIS);
> +}
> +
> +struct cs2600_clk_div {
> +       int div;
> +       unsigned int val;
> +};

Use struct clk_div_table? Doubtful the 'div' needs to be signed.

> +
> +static const struct cs2600_clk_div cs2600_bclk_div[] = {
> +       { 1, CS2600_BCLK_DIV(0x0) },
> +       { 2, CS2600_BCLK_DIV(0x1) },
> +       { 3, CS2600_BCLK_DIV(0x2) },
> +       { 4, CS2600_BCLK_DIV(0x3) },
> +       { 6, CS2600_BCLK_DIV(0x4) },
> +       { 8, CS2600_BCLK_DIV(0x5) },
> +       { 12, CS2600_BCLK_DIV(0x6) },
> +       { 16, CS2600_BCLK_DIV(0x7) },
> +       { 24, CS2600_BCLK_DIV(0x8) },
> +       { 32, CS2600_BCLK_DIV(0x9) },
> +       { 48, CS2600_BCLK_DIV(0xA) }
> +};
> +
[...]
> +
> +static int cs2600_fsync_determine_rate(struct clk_hw *hw,
> +                                      struct clk_rate_request *req)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       unsigned long parent_rate, freq_var, calc_rate;
> +       unsigned long ppm = CS2600_12_20_PPM;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +       int i;
> +
> +       if (req->rate > CS2600_FSYNC_OUT_MAX

Style nitpick, the || goes on the line above.

> +           || req->rate < CS2600_FSYNC_OUT_MIN) {
> +               dev_err(cs2600->dev, "Invalid fsync rate %lu", req->rate);

These error messages need newlines in them.

> +               return -EINVAL;
> +       }
> +
> +       if (regmap_test_bits(cs2600->regmap, CS2600_PLL_CFG3, CS2600_RATIO_CFG))
> +               ppm = CS2600_20_12_PPM;
> +
> +       freq_var = cs2600_get_freq_variation(cs2600, req->rate, ppm);
> +
> +       for (i = 0; i < ARRAY_SIZE(cs2600_fsync_div); i++) {
> +               calc_rate = req->best_parent_rate / cs2600_fsync_div[i].div;
> +               if (in_range(req->rate, calc_rate - freq_var,
> +                   calc_rate + freq_var))
> +                       return 0;
> +       }
> +
> +       if (req->best_parent_rate) {
> +               dev_err(cs2600->dev,
> +                       "FSYNC_OUT cannot be derived from the parent rate");
> +               return -EPERM;
> +       }
> +
> +       /* Change parent rate to accommodate the FSYNC rate */
> +
> +       for (i = 0; i < ARRAY_SIZE(cs2600_fsync_div); i++) {
> +               parent_rate = req->rate * cs2600_fsync_div[i].div;
> +               if (parent_rate > CS2600_CLK_OUT_MAX ||
> +                   parent_rate < CS2600_CLK_OUT_MIN)
> +                       continue;
> +
> +               req->best_parent_rate = parent_rate;
> +               return 0;
> +       }
> +
> +       dev_err(cs2600->dev, "Cannot determine parent rate\n");
> +       return -EINVAL;
> +}
> +
> +static unsigned long cs2600_fsync_recalc_rate(struct clk_hw *hw,
> +                                             unsigned long parent_rate)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +       unsigned int val;
> +       int i;
> +
> +       regmap_read(regmap, CS2600_OUTPUT_CFG1, &val);
> +
> +       for (i = 0; i < ARRAY_SIZE(cs2600_fsync_div); i++) {
> +               if (cs2600_fsync_div[i].val == (val & CS2600_FSYNC_DIV_MASK))
> +                       return (parent_rate / cs2600_fsync_div[i].div);
> +       }
> +
> +       dev_err(cs2600->dev, "Cannot find fsync divide value\n");
> +       return 0;
> +}
> +
> +static int cs2600_fsync_prepare(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +
> +       regmap_set_bits(regmap, CS2600_PHASE_ALIGNMENT_CFG1,
> +                       CS2600_PHASE_ALIGN_EN | CS2600_PHASE_ALIGN_STB_EN);
> +
> +       cs2600_set_freeze(cs2600);
> +       regmap_clear_bits(regmap, CS2600_OUTPUT_CFG1, CS2600_FSYNC_OUT_DIS);
> +       cs2600_clear_freeze(cs2600);
> +
> +       return 0;
> +}
> +
> +static void cs2600_fsync_unprepare(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +       struct cs2600 *cs2600 = clk_hw->priv;
> +
> +       if (regmap_test_bits(regmap, CS2600_OUTPUT_CFG1, CS2600_BCLK_OUT_DIS))
> +               regmap_clear_bits(regmap, CS2600_PHASE_ALIGNMENT_CFG1,
> +                                 CS2600_PHASE_ALIGN_EN |
> +                                 CS2600_PHASE_ALIGN_STB_EN);
> +
> +       cs2600_set_freeze(cs2600);
> +       regmap_set_bits(regmap, CS2600_OUTPUT_CFG1, CS2600_FSYNC_OUT_DIS);

Does regmap support an unlock and lock sequence? Maybe the regmap can
have cs2600_set_freeze() as the "lock" callback and
cs2600_clear_freeze() as the "unlock" callback? That could grab a mutex
as well. Then we can just have regmap calls here instead of the
set/clear braces.

> +       cs2600_clear_freeze(cs2600);
> +}
> +
> +static int cs2600_fsync_is_prepared(struct clk_hw *hw)
> +{
> +       struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
> +       struct regmap *regmap = clk_hw->priv->regmap;
> +
> +       return !regmap_test_bits(regmap, CS2600_OUTPUT_CFG1,
> +                                CS2600_FSYNC_OUT_DIS);
> +}
> +
[...]
> +
> +static int cs2600_clk_get(struct cs2600 *cs2600)
> +{
> +       struct clk *clk_in, *ref_clk;
> +
> +       clk_in = devm_clk_get_optional(cs2600->dev, "clk_in");
> +       if (IS_ERR(clk_in))
> +               return PTR_ERR(clk_in);
> +
> +       ref_clk = devm_clk_get_optional(cs2600->dev, "xti");
> +       if (IS_ERR(ref_clk))
> +               return PTR_ERR(ref_clk);
> +
> +       cs2600->ref_clk = ref_clk;
> +       cs2600->clk_in = clk_in;
> +
> +       return 0;
> +}
> +
> +static struct clk_hw *cs2600_of_clk_get(struct of_phandle_args *clkspec,
> +                                       void *data)
> +{
> +       unsigned int idx = clkspec->args[0];
> +       struct cs2600 *cs2600 = data;
> +
> +       /* PLL_OUT is an internal clock */
> +       if (idx > CS2600_OUT_CLK_MAX - 1) {
> +               dev_err(cs2600->dev, "Invalid clock index %d\n", idx);
> +               return ERR_PTR(-EINVAL);
> +       }
> +
> +       return &cs2600->hw[idx].hw;
> +}
> +
> +static int cs2600_clk_register(struct cs2600 *cs2600)
> +{
> +       struct device_node *np = cs2600->dev->of_node;
> +       static const char *parent_names[2];
> +       int ret, i, input_num = 0;
> +       const char *name;
> +
> +       if (cs2600->ref_clk)
> +               parent_names[input_num++] = __clk_get_name(cs2600->ref_clk);
> +
> +       if (cs2600->clk_in)
> +               parent_names[input_num++] = __clk_get_name(cs2600->clk_in);

Use struct clk_parent_data with dt index numbers instead of clk_get()
and __clk_get_name().

> +
> +       cs2600->hw[CS2600_PLL_OUT].init.name = cs2600_clks[CS2600_PLL_OUT].name;
> +       cs2600->hw[CS2600_PLL_OUT].init.ops = &cs2600_clks[CS2600_PLL_OUT].ops;
> +       cs2600->hw[CS2600_PLL_OUT].init.parent_names = parent_names;
> +       cs2600->hw[CS2600_PLL_OUT].init.num_parents = input_num;
> +       cs2600->hw[CS2600_PLL_OUT].hw.init = &cs2600->hw[CS2600_PLL_OUT].init;
> +       cs2600->hw[CS2600_PLL_OUT].priv = cs2600;
> +
> +       ret = devm_clk_hw_register(cs2600->dev, &cs2600->hw[CS2600_PLL_OUT].hw);
> +       if (ret)
> +               return ret;
> +
> +       for (i = 1; i < ARRAY_SIZE(cs2600->hw); i++) {
> +               if (of_property_read_string_index(np, "clock-output-names",
> +                   i - 1, &name))
> +                       name = cs2600_clks[i].name;

Preferably this isn't used, because the property should be optional. Can
you make up some name by combining the device name and the clk name?

> +
> +               cs2600->hw[i].init.name = name;
> +               cs2600->hw[i].init.ops = &cs2600_clks[i].ops;
> +
> +               /* CLK_OUT is the only parent for BCLK_OUT and FSYNC_OUT */
> +               cs2600->hw[i].init.parent_names =
> +                                       &cs2600->hw[CS2600_PLL_OUT].init.name;
> +               cs2600->hw[i].init.num_parents = 1;
> +               cs2600->hw[i].init.flags = cs2600_clks[i].flags;
> +               cs2600->hw[i].hw.init = &cs2600->hw[i].init;
> +               cs2600->hw[i].priv = cs2600;
> +
> +               ret = devm_clk_hw_register(cs2600->dev, &cs2600->hw[i].hw);
> +               if (ret)
> +                       return ret;

No dev_err_probe() for this one?

> +       }
> +
> +       return devm_of_clk_add_hw_provider(cs2600->dev, cs2600_of_clk_get,
> +                                          cs2600);

No dev_err_probe() for this one?

> +}
> +
> +static int cs2600_set_mode(struct cs2600 *cs2600, const char *mode)
> +{
> +       if (!strncmp(mode, "smart_mode", sizeof("smart_mode")))
> +               cs2600->mode = CS2600_SMART_MODE;
> +       else if (!strncmp(mode, "smart_clkin_only_mode",
> +                sizeof("SMART_CLKIN_ONLY_MODE")))
> +               cs2600->mode = CS2600_SMART_CLKIN_ONLY_MODE;
> +       else
> +               return dev_err_probe(cs2600->dev, -EINVAL,
> +                                    "Invalid clock mode %s\n", mode);
> +
> +       return 0;
> +}
> +
> +static int cs2600_set_aux_output(struct cs2600 *cs2600, const char *aux_out)
> +{
> +       unsigned int val;
> +
> +       if (!strncmp(aux_out, "freq_unlock", sizeof("freq_unlock")))
> +               val = CS2600_AUX_OUT_FREQ_UNLOCK_VAL;
> +       else if (!strncmp(aux_out, "phase_unlock", sizeof("phase_unlock")))
> +               val = CS2600_AUX_OUT_PHASE_UNLOCK_VAL;
> +       else if (!strncmp(aux_out, "no_clkin", sizeof("no_clkin")))
> +               val = CS2600_AUX_OUT_NO_CLKIN_VAL;
> +       else
> +               return dev_err_probe(cs2600->dev, -EINVAL,
> +                                    "Invalid aux output %s\n", aux_out);
> +
> +       return regmap_update_bits(cs2600->regmap, CS2600_OUTPUT_CFG2,
> +                                 CS2600_AUX1OUT_SEL, CS2600_AUXOUT1_SRC(val));
> +}
> +
> +static int cs2600_parse_dt_params(struct cs2600 *cs2600)
> +{
> +       struct regmap *regmap = cs2600->regmap;
> +       struct device *dev = cs2600->dev;
> +       const char *val;
> +       int ret = 0;
> +
> +       cs2600_set_freeze(cs2600);
> +       if (!device_property_read_string(dev, "cirrus,aux-output-source", &val)) {

It looks like we can use device_property_match_property_string() instead and then
the code is clearer because we can index into the array. Hopefully a
NULL entry also works so we can have something like

	const char * const output_source[] = {
		[CS2600_AUX_OUT_FREQ_UNLOCK_VAL] = "freq_unlock",
		[CS2600_AUX_OUT_PHASE_UNLOCK_VAL] = "phase_unlock",
		[CS2600_AUX_OUT_NO_CLKIN_VAL] = "no_clkin"
	};

	val = device_property_match_property_string(dev, "cirrus,aux-output-source", 
		output_source, ARRAY_SIZE(output_source);
	if (val < 0)
		return val;

	ret = cs2600_set_aux_output(cs2600, val);

> +               ret = cs2600_set_aux_output(cs2600, val);
> +               if (ret) {
> +                       cs2600_clear_freeze(cs2600);
> +                       return ret;
> +               }
> +       } else {
> +               /* Set to HiZ if not used */
> +               regmap_set_bits(regmap, CS2600_PLL_CFG1, CS2600_AUX1_OUT_DIS);
> +       }
> +
> +       if (!device_property_read_string(dev, "cirrus,clock-mode", &val))
> +               ret = cs2600_set_mode(cs2600, val);
> +
> +       cs2600_clear_freeze(cs2600);
> +
> +       return ret;
> +}
> +
> +static int cs2600_check_device_id(struct cs2600 *cs2600)
> +{
> +       struct device *dev = cs2600->dev;
> +       unsigned int dev_id, rev;
> +       int ret;
> +
> +       ret = regmap_read(cs2600->regmap, CS2600_DEVICE_ID1, &dev_id);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Can't read device ID\n");
> +
> +       if (dev_id != CS2600_DEVICE_ID_VALUE)
> +               return dev_err_probe(dev, -ENODEV, "Invalid device id 0x%x\n",
> +                                    dev_id);
> +
> +       ret = regmap_read(cs2600->regmap, CS2600_DEVICE_ID2, &rev);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Can't read device revision\n");
> +
> +       dev_dbg(dev, "Device ID %x Rev %x", dev_id, rev);
> +
> +       return 0;
> +}
> +
> +static int cs2600_i2c_probe(struct i2c_client *client)
> +{
> +       struct device *dev = &client->dev;
> +       struct cs2600 *cs2600;
> +       int ret;
> +
> +       cs2600 = devm_kzalloc(dev, sizeof(*cs2600), GFP_KERNEL);
> +       if (!cs2600)
> +               return -ENOMEM;
> +
> +       ret = devm_regulator_get_enable(dev, "vdd");
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Error with vdd supply\n");
> +
> +       cs2600->dev = dev;
> +       i2c_set_clientdata(client, cs2600);
> +
> +       cs2600->regmap = devm_regmap_init_i2c(client, &cs2600_regmap_config);
> +       if (IS_ERR(cs2600->regmap))
> +               return dev_err_probe(dev, PTR_ERR(cs2600->regmap),
> +                                    "Regmap not created\n");
> +
> +       /* Required to wait at least 20ms after vdd is enabled */
> +       usleep_range(20000, 21000);
> +       ret = cs2600_check_device_id(cs2600);
> +       if (ret)
> +               return ret;
> +
> +       ret = regmap_write(cs2600->regmap, CS2600_SW_RESET, CS2600_SW_RST_VAL);
> +       if (ret)
> +               return ret;
> +
> +       /* Required to wait at least 5ms after software reset */
> +       usleep_range(5000, 6000);

Please use async probe if we're going to sleep in probe.

> +       ret = cs2600_clk_get(cs2600);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Invalid parent clocks\n");
> +
> +       /* Set output clocks to HiZ */
> +       cs2600_set_freeze(cs2600);
> +       regmap_set_bits(cs2600->regmap, CS2600_PLL_CFG1, CS2600_CLK_OUT_DIS);
> +       regmap_set_bits(cs2600->regmap, CS2600_OUTPUT_CFG1,
> +                       CS2600_BCLK_OUT_DIS | CS2600_FSYNC_OUT_DIS);
> +       cs2600_clear_freeze(cs2600);
> +
> +       ret = cs2600_parse_dt_params(cs2600);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Cannot parse dt params\n");
> +
> +       ret = cs2600_clk_register(cs2600);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Cannot register clocks\n");
> +
> +       if (cs2600->ref_clk) {
> +               cs2600->refclk_rate = clk_get_rate(cs2600->ref_clk);

The ref clk rate should be parent_rate in the clk_ops::set_rate().

> +               regmap_update_bits(cs2600->regmap, CS2600_PLL_CFG3,
> +                                  CS2600_SYSCLK_SRC_MASK,
> +                                  CS2600_SYSCLK_SRC_REFCLK);
> +       } else {
> +               cs2600->refclk_rate = CS2600_INTERNAL_OSC_RATE;
> +               regmap_update_bits(cs2600->regmap, CS2600_PLL_CFG3,
> +                                  CS2600_SYSCLK_SRC_MASK,
> +                                  CS2600_SYSCLK_SRC_OSC);
> +       }





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux