All of lore.kernel.org
 help / color / mirror / Atom feed
From: Haylen Chu <heylenay@4d2.org>
To: Stephen Boyd <sboyd@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Haylen Chu <heylenay@outlook.com>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Michael Turquette <mturquette@baylibre.com>,
	Rob Herring <robh@kernel.org>
Cc: linux-riscv@lists.infradead.org, linux-clk@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	Inochi Amaoto <inochiama@outlook.com>,
	Chen Wang <unicornxdotw@foxmail.com>,
	Jisheng Zhang <jszhang@kernel.org>
Subject: Re: [PATCH v2 3/3] clk: spacemit: Add clock support for Spacemit K1 SoC
Date: Wed, 27 Nov 2024 11:20:28 +0000	[thread overview]
Message-ID: <Z0cAfKdexQugojoJ@ketchup> (raw)
In-Reply-To: <a680b539e815f2e38f23126fede76591.sboyd@kernel.org>

Hi Stephen,

Sorry for such a late reply. FYI, I have sent a v3 and applied most of
your recommendation.

On Thu, Sep 19, 2024 at 04:08:32PM -0700, Stephen Boyd wrote:
> Quoting Haylen Chu (2024-09-16 15:23:10)
> > +static const char * const apb_parent_names[] = {
> 
> Please don't use strings for parents. Either use struct clk_parent_data
> or clk_hw pointers directly.
> 
> > +       "pll1_d96_25p6", "pll1_d48_51p2", "pll1_d96_25p6", "pll1_d24_102p4"
> > +};

Thanks for the hint, all parents are described with struct
clk_parent_data in v3.

> > +static int k1_ccu_probe(struct platform_device *pdev)
> > +{
> > +       const struct spacemit_ccu_data *data;
> > +       struct regmap *base_map, *lock_map;
> > +       struct device *dev = &pdev->dev;
> > +       struct spacemit_ccu_priv *priv;
> > +       struct device_node *parent;
> > +       int ret;
> > +
> > +       data = of_device_get_match_data(dev);
> > +       if (WARN_ON(!data))
> > +               return -EINVAL;
> > +
> > +       parent   = of_get_parent(dev->of_node);
> > +       base_map = syscon_node_to_regmap(parent);
> > +       of_node_put(parent);
> > +
> > +       if (IS_ERR(base_map))
> > +               return dev_err_probe(dev, PTR_ERR(base_map),
> > +                                    "failed to get regmap\n");
> > +
> > +       if (data->need_pll_lock) {
> > +               lock_map = syscon_regmap_lookup_by_phandle(dev->of_node,
> > +                                                          "spacemit,mpmu");
> > +               if (IS_ERR(lock_map))
> > +                       return dev_err_probe(dev, PTR_ERR(lock_map),
> > +                                            "failed to get lock regmap\n");
> > +       }
> > +
> > +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +       if (!priv)
> > +               return -ENOMEM;
> > +
> > +       priv->data      = data;
> > +       priv->base      = base_map;
> > +       priv->lock_base = lock_map;
> > +       spin_lock_init(&priv->lock);
> > +
> > +       ret = spacemit_ccu_register(dev, priv);
> > +       if (ret)
> > +               return dev_err_probe(dev, ret, "failed to register clocks");
> 
> Missing newline on printk

Corrected in v3.

> > diff --git a/drivers/clk/spacemit/ccu_ddn.c b/drivers/clk/spacemit/ccu_ddn.c
> > +static void ccu_ddn_disable(struct clk_hw *hw)
> > +{
> > +       struct ccu_ddn *ddn = hw_to_ccu_ddn(hw);
> > +       struct ccu_common *common = &ddn->common;
> > +       unsigned long flags;
> > +
> > +       if (!ddn->gate)
> > +               return;
> > +
> > +       spin_lock_irqsave(common->lock, flags);
> 
> The regmap can have a lock. Can you use that?

Thanks for the hint. This extra lock is dropped in v3. Since all
register operations to shared MMIO regions are performed through
regmap_update_bits(), there cannot be a race.

> > diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c
> > new file mode 100644
> > index 000000000000..750882b6ed93
> > --- /dev/null
> > +++ b/drivers/clk/spacemit/ccu_mix.c
> > +const struct clk_ops spacemit_ccu_mix_ops = {
> > +       .disable         = ccu_mix_disable,
> > +       .enable          = ccu_mix_enable,
> > +       .is_enabled      = ccu_mix_is_enabled,
> > +       .get_parent      = ccu_mix_get_parent,
> > +       .set_parent      = ccu_mix_set_parent,
> > +       .determine_rate  = ccu_mix_determine_rate,
> > +       .round_rate      = ccu_mix_round_rate,
> 
> Only implement determine_rate

Okay, duplicated round_rate is deleted in v3.

> 
> > +       .recalc_rate     = ccu_mix_recalc_rate,
> > +       .set_rate        = ccu_mix_set_rate,
> > +};
> > +
> > diff --git a/drivers/clk/spacemit/ccu_pll.c b/drivers/clk/spacemit/ccu_pll.c
> > new file mode 100644
> > index 000000000000..1f0ece6abcac
> > --- /dev/null
> > +++ b/drivers/clk/spacemit/ccu_pll.c
> > @@ -0,0 +1,226 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Spacemit clock type pll
> > + *
> > + * Copyright (c) 2024 SpacemiT Technology Co. Ltd
> > + * Copyright (c) 2024 Haylen Chu <heylenay@outlook.com>
> > + */
> > +
> > +#include <linux/clk-provider.h>
> > +#include <linux/regmap.h>
> > +
> > +#include "ccu_common.h"
> > +#include "ccu_pll.h"
> > +
> > +#define PLL_MIN_FREQ   600000000
> > +#define PLL_MAX_FREQ   3400000000
> > +#define PLL_DELAY_TIME 3000
> > +
> > +#define pll_read_swcr1(c, v)   ccu_read(ctrl, c, v)
> > +#define pll_read_swcr2(c, v)   ccu_read(sel, c, v)
> > +#define pll_read_swcr3(c, v)   ccu_read(xtc, c, v)
> > +
> > +#define pll_update_swcr1(c, m, v)      ccu_update(ctrl, c, m, v)
> > +#define pll_update_swcr2(c, m, v)      ccu_update(sel, c, m, v)
> > +#define pll_update_swcr3(c, m, v)      ccu_update(xtc, c, m, v)
> 
> Please stop wrapping regmap APIs. Just use them directly.

Thanks, I drop the regmap wrappers for each clock type, but keep
ccu_{read,update} in v3 since they save a lot of keystrokes and make
the code easier to read.

> 
> > +
> > +#define PLL_SWCR1_REG5_OFF     0
> > +#define PLL_SWCR1_REG5_MASK    GENMASK(7, 0)
> > +#define PLL_SWCR1_REG6_OFF     8
> > +#define PLL_SWCR1_REG6_MASK    GENMASK(15, 8)
> > +#define PLL_SWCR1_REG7_OFF     16
> > +#define PLL_SWCR1_REG7_MASK    GENMASK(23, 16)
> > +#define PLL_SWCR1_REG8_OFF     24
> > +#define PLL_SWCR1_REG8_MASK    GENMASK(31, 24)
> > +
> > +#define PLL_SWCR2_DIVn_EN(n)   BIT(n + 1)
> > +#define PLL_SWCR2_ATEST_EN     BIT(12)
> > +#define PLL_SWCR2_CKTEST_EN    BIT(13)
> > +#define PLL_SWCR2_DTEST_EN     BIT(14)
> > +
> > +#define PLL_SWCR3_DIV_FRC_OFF  0
> > +#define PLL_SWCR3_DIV_FRC_MASK GENMASK(23, 0)
> > +#define PLL_SWCR3_DIV_INT_OFF  24
> > +#define PLL_SWCR3_DIV_INT_MASK GENMASK(30, 24)
> > +#define PLL_SWCR3_EN           BIT(31)
> > +
> > +static int ccu_pll_is_enabled(struct clk_hw *hw)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       u32 tmp;
> > +
> > +       pll_read_swcr3(&p->common, &tmp);
> > +
> > +       return tmp & PLL_SWCR3_EN;
> > +}
> > +
> > +/* frequency unit Mhz, return pll vco freq */
> > +static unsigned long __get_vco_freq(struct clk_hw *hw)
> > +{
> > +       unsigned int reg5, reg6, reg7, reg8, size, i;
> > +       unsigned int div_int, div_frc;
> > +       struct ccu_pll_rate_tbl *freq_pll_regs_table;
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_common *common = &p->common;
> > +       u32 tmp;
> > +
> > +       pll_read_swcr1(common, &tmp);
> > +       reg5 = (tmp & PLL_SWCR1_REG5_MASK) >> PLL_SWCR1_REG5_OFF;
> > +       reg6 = (tmp & PLL_SWCR1_REG6_MASK) >> PLL_SWCR1_REG6_OFF;
> > +       reg7 = (tmp & PLL_SWCR1_REG7_MASK) >> PLL_SWCR1_REG7_OFF;
> > +       reg8 = (tmp & PLL_SWCR1_REG8_MASK) >> PLL_SWCR1_REG8_OFF;
> > +
> > +       pll_read_swcr3(common, &tmp);
> > +       div_int = (tmp & PLL_SWCR3_DIV_INT_MASK) >> PLL_SWCR3_DIV_INT_OFF;
> > +       div_frc = (tmp & PLL_SWCR3_DIV_FRC_MASK) >> PLL_SWCR3_DIV_FRC_OFF;
> > +
> > +       freq_pll_regs_table = p->pll.rate_tbl;
> > +       size = p->pll.tbl_size;
> > +
> > +       for (i = 0; i < size; i++)
> > +               if ((freq_pll_regs_table[i].reg5 == reg5) &&
> > +                   (freq_pll_regs_table[i].reg6 == reg6) &&
> > +                   (freq_pll_regs_table[i].reg7 == reg7) &&
> > +                   (freq_pll_regs_table[i].reg8 == reg8) &&
> > +                   (freq_pll_regs_table[i].div_int == div_int) &&
> > +                   (freq_pll_regs_table[i].div_frac == div_frc))
> > +                       return freq_pll_regs_table[i].rate;
> > +
> > +       WARN_ON_ONCE(1);
> > +
> > +       return 0;
> > +}
> > +
> > +static int ccu_pll_enable(struct clk_hw *hw)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_common *common = &p->common;
> > +       unsigned long flags;
> > +       unsigned int tmp;
> > +       int ret;
> > +
> > +       if (ccu_pll_is_enabled(hw))
> > +               return 0;
> > +
> > +       spin_lock_irqsave(common->lock, flags);
> > +
> > +       pll_update_swcr3(common, PLL_SWCR3_EN, PLL_SWCR3_EN);
> > +
> > +       spin_unlock_irqrestore(common->lock, flags);
> > +
> > +       /* check lock status */
> > +       ret = regmap_read_poll_timeout_atomic(common->lock_base,
> > +                                             p->pll.reg_lock,
> > +                                             tmp,
> > +                                             tmp & p->pll.lock_enable_bit,
> > +                                             5, PLL_DELAY_TIME);
> > +
> > +       return ret;
> > +}
> > +
> > +static void ccu_pll_disable(struct clk_hw *hw)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_common *common = &p->common;
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(p->common.lock, flags);
> > +
> > +       pll_update_swcr3(common, PLL_SWCR3_EN, 0);
> > +
> > +       spin_unlock_irqrestore(common->lock, flags);
> > +}
> > +
> > +/*
> > + * pll rate change requires sequence:
> > + * clock off -> change rate setting -> clock on
> > + * This function doesn't really change rate, but cache the config
> > + */
> > +static int ccu_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> > +                              unsigned long parent_rate)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_common *common = &p->common;
> > +       struct ccu_pll_config *params = &p->pll;
> > +       struct ccu_pll_rate_tbl *entry;
> > +       unsigned long old_rate;
> > +       unsigned long flags;
> > +       bool found = false;
> > +       u32 mask, val;
> > +       int i;
> > +
> > +       if (ccu_pll_is_enabled(hw)) {
> > +               pr_err("%s %s is enabled, ignore the setrate!\n",
> > +                      __func__, __clk_get_name(hw->clk));
> > +               return 0;
> > +       }
> > +
> > +       old_rate = __get_vco_freq(hw);
> > +
> > +       for (i = 0; i < params->tbl_size; i++) {
> > +               if (rate == params->rate_tbl[i].rate) {
> > +                       found = true;
> > +                       entry = &params->rate_tbl[i];
> > +                       break;
> > +               }
> > +       }
> > +       WARN_ON_ONCE(!found);
> > +
> > +       spin_lock_irqsave(common->lock, flags);
> > +
> > +       mask = PLL_SWCR1_REG5_MASK | PLL_SWCR1_REG6_MASK;
> > +       mask |= PLL_SWCR1_REG7_MASK | PLL_SWCR1_REG8_MASK;
> > +       val |= entry->reg5 << PLL_SWCR1_REG5_OFF;
> > +       val |= entry->reg6 << PLL_SWCR1_REG6_OFF;
> > +       val |= entry->reg7 << PLL_SWCR1_REG7_OFF;
> > +       val |= entry->reg8 << PLL_SWCR1_REG8_OFF;
> > +       pll_update_swcr1(common, mask, val);
> > +
> > +       mask = PLL_SWCR3_DIV_INT_MASK | PLL_SWCR3_DIV_FRC_MASK;
> > +       val = entry->div_int << PLL_SWCR3_DIV_INT_OFF;
> > +       val |= entry->div_frac << PLL_SWCR3_DIV_FRC_OFF;
> > +       pll_update_swcr3(common, mask, val);
> > +
> > +       spin_unlock_irqrestore(common->lock, flags);
> > +
> > +       return 0;
> > +}
> > +
> > +static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw,
> > +                                        unsigned long parent_rate)
> > +{
> > +       return __get_vco_freq(hw);
> > +}
> > +
> > +static long ccu_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> > +                              unsigned long *prate)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_pll_config *params = &p->pll;
> > +       unsigned long max_rate = 0;
> > +       unsigned int i;
> > +
> > +       if (rate > PLL_MAX_FREQ || rate < PLL_MIN_FREQ) {
> > +               pr_err("%lu rate out of range!\n", rate);
> 
> We should simply clamp the rate here. It doesn't matter what 'rate' is
> when this function is called. The callback is supposed to determine what
> the clk rate will be if a consumer called clk_set_rate() with 'rate'.
> Don't fail that if the rate is requested to be larger than max, just
> tell clk_round_rate() that if you ask for something larger you'll get
> PLL_MAX_FREQ.

Thanks for explaining the convention. I have adapted ccu_pll_round_rate
to follow this behavior in v3.

> > +               return -EINVAL;
> > +       }
> > +

Thanks again for your review and time.

Best regards,
Haylen Chu

WARNING: multiple messages have this Message-ID (diff)
From: Haylen Chu <heylenay@4d2.org>
To: Stephen Boyd <sboyd@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Haylen Chu <heylenay@outlook.com>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Michael Turquette <mturquette@baylibre.com>,
	Rob Herring <robh@kernel.org>
Cc: linux-riscv@lists.infradead.org, linux-clk@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	Inochi Amaoto <inochiama@outlook.com>,
	Chen Wang <unicornxdotw@foxmail.com>,
	Jisheng Zhang <jszhang@kernel.org>
Subject: Re: [PATCH v2 3/3] clk: spacemit: Add clock support for Spacemit K1 SoC
Date: Wed, 27 Nov 2024 11:20:28 +0000	[thread overview]
Message-ID: <Z0cAfKdexQugojoJ@ketchup> (raw)
In-Reply-To: <a680b539e815f2e38f23126fede76591.sboyd@kernel.org>

Hi Stephen,

Sorry for such a late reply. FYI, I have sent a v3 and applied most of
your recommendation.

On Thu, Sep 19, 2024 at 04:08:32PM -0700, Stephen Boyd wrote:
> Quoting Haylen Chu (2024-09-16 15:23:10)
> > +static const char * const apb_parent_names[] = {
> 
> Please don't use strings for parents. Either use struct clk_parent_data
> or clk_hw pointers directly.
> 
> > +       "pll1_d96_25p6", "pll1_d48_51p2", "pll1_d96_25p6", "pll1_d24_102p4"
> > +};

Thanks for the hint, all parents are described with struct
clk_parent_data in v3.

> > +static int k1_ccu_probe(struct platform_device *pdev)
> > +{
> > +       const struct spacemit_ccu_data *data;
> > +       struct regmap *base_map, *lock_map;
> > +       struct device *dev = &pdev->dev;
> > +       struct spacemit_ccu_priv *priv;
> > +       struct device_node *parent;
> > +       int ret;
> > +
> > +       data = of_device_get_match_data(dev);
> > +       if (WARN_ON(!data))
> > +               return -EINVAL;
> > +
> > +       parent   = of_get_parent(dev->of_node);
> > +       base_map = syscon_node_to_regmap(parent);
> > +       of_node_put(parent);
> > +
> > +       if (IS_ERR(base_map))
> > +               return dev_err_probe(dev, PTR_ERR(base_map),
> > +                                    "failed to get regmap\n");
> > +
> > +       if (data->need_pll_lock) {
> > +               lock_map = syscon_regmap_lookup_by_phandle(dev->of_node,
> > +                                                          "spacemit,mpmu");
> > +               if (IS_ERR(lock_map))
> > +                       return dev_err_probe(dev, PTR_ERR(lock_map),
> > +                                            "failed to get lock regmap\n");
> > +       }
> > +
> > +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +       if (!priv)
> > +               return -ENOMEM;
> > +
> > +       priv->data      = data;
> > +       priv->base      = base_map;
> > +       priv->lock_base = lock_map;
> > +       spin_lock_init(&priv->lock);
> > +
> > +       ret = spacemit_ccu_register(dev, priv);
> > +       if (ret)
> > +               return dev_err_probe(dev, ret, "failed to register clocks");
> 
> Missing newline on printk

Corrected in v3.

> > diff --git a/drivers/clk/spacemit/ccu_ddn.c b/drivers/clk/spacemit/ccu_ddn.c
> > +static void ccu_ddn_disable(struct clk_hw *hw)
> > +{
> > +       struct ccu_ddn *ddn = hw_to_ccu_ddn(hw);
> > +       struct ccu_common *common = &ddn->common;
> > +       unsigned long flags;
> > +
> > +       if (!ddn->gate)
> > +               return;
> > +
> > +       spin_lock_irqsave(common->lock, flags);
> 
> The regmap can have a lock. Can you use that?

Thanks for the hint. This extra lock is dropped in v3. Since all
register operations to shared MMIO regions are performed through
regmap_update_bits(), there cannot be a race.

> > diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c
> > new file mode 100644
> > index 000000000000..750882b6ed93
> > --- /dev/null
> > +++ b/drivers/clk/spacemit/ccu_mix.c
> > +const struct clk_ops spacemit_ccu_mix_ops = {
> > +       .disable         = ccu_mix_disable,
> > +       .enable          = ccu_mix_enable,
> > +       .is_enabled      = ccu_mix_is_enabled,
> > +       .get_parent      = ccu_mix_get_parent,
> > +       .set_parent      = ccu_mix_set_parent,
> > +       .determine_rate  = ccu_mix_determine_rate,
> > +       .round_rate      = ccu_mix_round_rate,
> 
> Only implement determine_rate

Okay, duplicated round_rate is deleted in v3.

> 
> > +       .recalc_rate     = ccu_mix_recalc_rate,
> > +       .set_rate        = ccu_mix_set_rate,
> > +};
> > +
> > diff --git a/drivers/clk/spacemit/ccu_pll.c b/drivers/clk/spacemit/ccu_pll.c
> > new file mode 100644
> > index 000000000000..1f0ece6abcac
> > --- /dev/null
> > +++ b/drivers/clk/spacemit/ccu_pll.c
> > @@ -0,0 +1,226 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Spacemit clock type pll
> > + *
> > + * Copyright (c) 2024 SpacemiT Technology Co. Ltd
> > + * Copyright (c) 2024 Haylen Chu <heylenay@outlook.com>
> > + */
> > +
> > +#include <linux/clk-provider.h>
> > +#include <linux/regmap.h>
> > +
> > +#include "ccu_common.h"
> > +#include "ccu_pll.h"
> > +
> > +#define PLL_MIN_FREQ   600000000
> > +#define PLL_MAX_FREQ   3400000000
> > +#define PLL_DELAY_TIME 3000
> > +
> > +#define pll_read_swcr1(c, v)   ccu_read(ctrl, c, v)
> > +#define pll_read_swcr2(c, v)   ccu_read(sel, c, v)
> > +#define pll_read_swcr3(c, v)   ccu_read(xtc, c, v)
> > +
> > +#define pll_update_swcr1(c, m, v)      ccu_update(ctrl, c, m, v)
> > +#define pll_update_swcr2(c, m, v)      ccu_update(sel, c, m, v)
> > +#define pll_update_swcr3(c, m, v)      ccu_update(xtc, c, m, v)
> 
> Please stop wrapping regmap APIs. Just use them directly.

Thanks, I drop the regmap wrappers for each clock type, but keep
ccu_{read,update} in v3 since they save a lot of keystrokes and make
the code easier to read.

> 
> > +
> > +#define PLL_SWCR1_REG5_OFF     0
> > +#define PLL_SWCR1_REG5_MASK    GENMASK(7, 0)
> > +#define PLL_SWCR1_REG6_OFF     8
> > +#define PLL_SWCR1_REG6_MASK    GENMASK(15, 8)
> > +#define PLL_SWCR1_REG7_OFF     16
> > +#define PLL_SWCR1_REG7_MASK    GENMASK(23, 16)
> > +#define PLL_SWCR1_REG8_OFF     24
> > +#define PLL_SWCR1_REG8_MASK    GENMASK(31, 24)
> > +
> > +#define PLL_SWCR2_DIVn_EN(n)   BIT(n + 1)
> > +#define PLL_SWCR2_ATEST_EN     BIT(12)
> > +#define PLL_SWCR2_CKTEST_EN    BIT(13)
> > +#define PLL_SWCR2_DTEST_EN     BIT(14)
> > +
> > +#define PLL_SWCR3_DIV_FRC_OFF  0
> > +#define PLL_SWCR3_DIV_FRC_MASK GENMASK(23, 0)
> > +#define PLL_SWCR3_DIV_INT_OFF  24
> > +#define PLL_SWCR3_DIV_INT_MASK GENMASK(30, 24)
> > +#define PLL_SWCR3_EN           BIT(31)
> > +
> > +static int ccu_pll_is_enabled(struct clk_hw *hw)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       u32 tmp;
> > +
> > +       pll_read_swcr3(&p->common, &tmp);
> > +
> > +       return tmp & PLL_SWCR3_EN;
> > +}
> > +
> > +/* frequency unit Mhz, return pll vco freq */
> > +static unsigned long __get_vco_freq(struct clk_hw *hw)
> > +{
> > +       unsigned int reg5, reg6, reg7, reg8, size, i;
> > +       unsigned int div_int, div_frc;
> > +       struct ccu_pll_rate_tbl *freq_pll_regs_table;
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_common *common = &p->common;
> > +       u32 tmp;
> > +
> > +       pll_read_swcr1(common, &tmp);
> > +       reg5 = (tmp & PLL_SWCR1_REG5_MASK) >> PLL_SWCR1_REG5_OFF;
> > +       reg6 = (tmp & PLL_SWCR1_REG6_MASK) >> PLL_SWCR1_REG6_OFF;
> > +       reg7 = (tmp & PLL_SWCR1_REG7_MASK) >> PLL_SWCR1_REG7_OFF;
> > +       reg8 = (tmp & PLL_SWCR1_REG8_MASK) >> PLL_SWCR1_REG8_OFF;
> > +
> > +       pll_read_swcr3(common, &tmp);
> > +       div_int = (tmp & PLL_SWCR3_DIV_INT_MASK) >> PLL_SWCR3_DIV_INT_OFF;
> > +       div_frc = (tmp & PLL_SWCR3_DIV_FRC_MASK) >> PLL_SWCR3_DIV_FRC_OFF;
> > +
> > +       freq_pll_regs_table = p->pll.rate_tbl;
> > +       size = p->pll.tbl_size;
> > +
> > +       for (i = 0; i < size; i++)
> > +               if ((freq_pll_regs_table[i].reg5 == reg5) &&
> > +                   (freq_pll_regs_table[i].reg6 == reg6) &&
> > +                   (freq_pll_regs_table[i].reg7 == reg7) &&
> > +                   (freq_pll_regs_table[i].reg8 == reg8) &&
> > +                   (freq_pll_regs_table[i].div_int == div_int) &&
> > +                   (freq_pll_regs_table[i].div_frac == div_frc))
> > +                       return freq_pll_regs_table[i].rate;
> > +
> > +       WARN_ON_ONCE(1);
> > +
> > +       return 0;
> > +}
> > +
> > +static int ccu_pll_enable(struct clk_hw *hw)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_common *common = &p->common;
> > +       unsigned long flags;
> > +       unsigned int tmp;
> > +       int ret;
> > +
> > +       if (ccu_pll_is_enabled(hw))
> > +               return 0;
> > +
> > +       spin_lock_irqsave(common->lock, flags);
> > +
> > +       pll_update_swcr3(common, PLL_SWCR3_EN, PLL_SWCR3_EN);
> > +
> > +       spin_unlock_irqrestore(common->lock, flags);
> > +
> > +       /* check lock status */
> > +       ret = regmap_read_poll_timeout_atomic(common->lock_base,
> > +                                             p->pll.reg_lock,
> > +                                             tmp,
> > +                                             tmp & p->pll.lock_enable_bit,
> > +                                             5, PLL_DELAY_TIME);
> > +
> > +       return ret;
> > +}
> > +
> > +static void ccu_pll_disable(struct clk_hw *hw)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_common *common = &p->common;
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(p->common.lock, flags);
> > +
> > +       pll_update_swcr3(common, PLL_SWCR3_EN, 0);
> > +
> > +       spin_unlock_irqrestore(common->lock, flags);
> > +}
> > +
> > +/*
> > + * pll rate change requires sequence:
> > + * clock off -> change rate setting -> clock on
> > + * This function doesn't really change rate, but cache the config
> > + */
> > +static int ccu_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> > +                              unsigned long parent_rate)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_common *common = &p->common;
> > +       struct ccu_pll_config *params = &p->pll;
> > +       struct ccu_pll_rate_tbl *entry;
> > +       unsigned long old_rate;
> > +       unsigned long flags;
> > +       bool found = false;
> > +       u32 mask, val;
> > +       int i;
> > +
> > +       if (ccu_pll_is_enabled(hw)) {
> > +               pr_err("%s %s is enabled, ignore the setrate!\n",
> > +                      __func__, __clk_get_name(hw->clk));
> > +               return 0;
> > +       }
> > +
> > +       old_rate = __get_vco_freq(hw);
> > +
> > +       for (i = 0; i < params->tbl_size; i++) {
> > +               if (rate == params->rate_tbl[i].rate) {
> > +                       found = true;
> > +                       entry = &params->rate_tbl[i];
> > +                       break;
> > +               }
> > +       }
> > +       WARN_ON_ONCE(!found);
> > +
> > +       spin_lock_irqsave(common->lock, flags);
> > +
> > +       mask = PLL_SWCR1_REG5_MASK | PLL_SWCR1_REG6_MASK;
> > +       mask |= PLL_SWCR1_REG7_MASK | PLL_SWCR1_REG8_MASK;
> > +       val |= entry->reg5 << PLL_SWCR1_REG5_OFF;
> > +       val |= entry->reg6 << PLL_SWCR1_REG6_OFF;
> > +       val |= entry->reg7 << PLL_SWCR1_REG7_OFF;
> > +       val |= entry->reg8 << PLL_SWCR1_REG8_OFF;
> > +       pll_update_swcr1(common, mask, val);
> > +
> > +       mask = PLL_SWCR3_DIV_INT_MASK | PLL_SWCR3_DIV_FRC_MASK;
> > +       val = entry->div_int << PLL_SWCR3_DIV_INT_OFF;
> > +       val |= entry->div_frac << PLL_SWCR3_DIV_FRC_OFF;
> > +       pll_update_swcr3(common, mask, val);
> > +
> > +       spin_unlock_irqrestore(common->lock, flags);
> > +
> > +       return 0;
> > +}
> > +
> > +static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw,
> > +                                        unsigned long parent_rate)
> > +{
> > +       return __get_vco_freq(hw);
> > +}
> > +
> > +static long ccu_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> > +                              unsigned long *prate)
> > +{
> > +       struct ccu_pll *p = hw_to_ccu_pll(hw);
> > +       struct ccu_pll_config *params = &p->pll;
> > +       unsigned long max_rate = 0;
> > +       unsigned int i;
> > +
> > +       if (rate > PLL_MAX_FREQ || rate < PLL_MIN_FREQ) {
> > +               pr_err("%lu rate out of range!\n", rate);
> 
> We should simply clamp the rate here. It doesn't matter what 'rate' is
> when this function is called. The callback is supposed to determine what
> the clk rate will be if a consumer called clk_set_rate() with 'rate'.
> Don't fail that if the rate is requested to be larger than max, just
> tell clk_round_rate() that if you ask for something larger you'll get
> PLL_MAX_FREQ.

Thanks for explaining the convention. I have adapted ccu_pll_round_rate
to follow this behavior in v3.

> > +               return -EINVAL;
> > +       }
> > +

Thanks again for your review and time.

Best regards,
Haylen Chu

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

  reply	other threads:[~2024-11-27 11:20 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-09-16 22:20 [PATCH v2 0/3] Add clock controller support for Spacemit K1 Haylen Chu
2024-09-16 22:20 ` Haylen Chu
2024-09-16 22:23 ` [PATCH v2 1/3] dt-bindings: soc: spacemit: Add spacemit,k1-syscon Haylen Chu
2024-09-16 22:23   ` Haylen Chu
2024-09-16 23:27   ` Rob Herring (Arm)
2024-09-16 23:27     ` Rob Herring (Arm)
2024-09-17  6:15   ` Krzysztof Kozlowski
2024-09-17  6:15     ` Krzysztof Kozlowski
2024-09-16 22:23 ` [PATCH v2 2/3] dt-bindings: clock: spacemit: Add clock controllers of Spacemit K1 SoC Haylen Chu
2024-09-16 22:23   ` Haylen Chu
2024-09-16 23:27   ` Rob Herring (Arm)
2024-09-16 23:27     ` Rob Herring (Arm)
2024-09-17  6:39   ` Krzysztof Kozlowski
2024-09-17  6:39     ` Krzysztof Kozlowski
2024-10-16 12:10     ` Haylen Chu
2024-10-16 12:10       ` Haylen Chu
2024-10-16 12:57       ` Krzysztof Kozlowski
2024-10-16 12:57         ` Krzysztof Kozlowski
2024-09-16 22:23 ` [PATCH v2 3/3] clk: spacemit: Add clock support for " Haylen Chu
2024-09-16 22:23   ` Haylen Chu
2024-09-19 23:08   ` Stephen Boyd
2024-09-19 23:08     ` Stephen Boyd
2024-11-27 11:20     ` Haylen Chu [this message]
2024-11-27 11:20       ` Haylen Chu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Z0cAfKdexQugojoJ@ketchup \
    --to=heylenay@4d2.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=heylenay@outlook.com \
    --cc=inochiama@outlook.com \
    --cc=jszhang@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=mturquette@baylibre.com \
    --cc=robh@kernel.org \
    --cc=sboyd@kernel.org \
    --cc=unicornxdotw@foxmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.