From: Vinod Koul <vkoul@kernel.org>
To: Sean Anderson <sean.anderson@seco.com>
Cc: Kishon Vijay Abraham I <kishon@kernel.org>,
linux-phy@lists.infradead.org,
Madalin Bucur <madalin.bucur@nxp.com>,
linux-arm-kernel@lists.infradead.org,
Camelia Alexandra Groza <camelia.groza@nxp.com>,
devicetree@vger.kernel.org, Rob Herring <robh+dt@kernel.org>,
linuxppc-dev@lists.ozlabs.org,
Bagas Sanjaya <bagasdotme@gmail.com>,
Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
Ioana Ciornei <ioana.ciornei@nxp.com>,
Michael Turquette <mturquette@baylibre.com>,
Stephen Boyd <sboyd@kernel.org>,
linux-clk@vger.kernel.org
Subject: Re: [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver
Date: Mon, 8 May 2023 14:45:35 +0530 [thread overview]
Message-ID: <ZFi9t84UoIfUyHhi@matsya> (raw)
In-Reply-To: <20230413160607.4128315-7-sean.anderson@seco.com>
On 13-04-23, 12:05, Sean Anderson wrote:
> This adds support for the PLLs found in Lynx 10G "SerDes" devices found on
> various NXP QorIQ SoCs. There are two PLLs in each SerDes. This driver has
> been split from the main PHY driver to allow for better review, even though
> these PLLs are not present anywhere else besides the SerDes. An auxiliary
> device is not used as it offers no benefits over a function call (and there
> is no need to have a separate device).
>
> The PLLs are modeled as clocks proper to let us take advantage of the
> existing clock infrastructure. I have not given the same treatment to the
> per-lane clocks because they need to be programmed in-concert with the rest
> of the lane settings. One tricky thing is that the VCO (PLL) rate exceeds
> 2^32 (maxing out at around 5GHz). This will be a problem on 32-bit
> platforms, since clock rates are stored as unsigned longs. To work around
> this, the pll clock rate is generally treated in units of kHz.
>
> The PLLs are configured rather interestingly. Instead of the usual direct
> programming of the appropriate divisors, the input and output clock rates
> are selected directly. Generally, the only restriction is that the input
> and output must be integer multiples of each other. This suggests some kind
> of internal look-up table. The datasheets generally list out the supported
> combinations explicitly, and not all input/output combinations are
> documented. I'm not sure if this is due to lack of support, or due to an
> oversight. If this becomes an issue, then some combinations can be
> blacklisted (or whitelisted). This may also be necessary for other SoCs
> which have more stringent clock requirements.
>
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>
> ---
>
> (no changes since v10)
>
> Changes in v10:
> - Remove unnecessary inclusion of clk.h
> - Don't gate clocks in compatibility mode
>
> Changes in v9:
> - Convert some u32s to unsigned long to match arguments
> - Switch from round_rate to determine_rate
> - Drop explicit reference to reference clock
> - Use .parent_names when requesting parents
> - Use devm_clk_hw_get_clk to pass clocks back to serdes
> - Fix indentation
> - Split off from following patch to allow for better review
>
> MAINTAINERS | 7 +
> drivers/clk/Makefile | 1 +
> drivers/clk/clk-fsl-lynx-10g.c | 510 +++++++++++++++++++++++++++++++++
> drivers/phy/freescale/Kconfig | 6 +
> include/linux/phy/lynx-10g.h | 16 ++
> 5 files changed, 540 insertions(+)
> create mode 100644 drivers/clk/clk-fsl-lynx-10g.c
> create mode 100644 include/linux/phy/lynx-10g.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index fce67b74e4a2..8da893681de6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12195,6 +12195,13 @@ S: Maintained
> W: http://linux-test-project.github.io/
> T: git https://github.com/linux-test-project/ltp.git
>
> +LYNX 10G SERDES DRIVER
> +M: Sean Anderson <sean.anderson@seco.com>
> +S: Maintained
> +F: drivers/clk/clk-fsl-lynx-10g.c
> +F: include/dt-bindings/clock/fsl,lynx-10g.h
> +F: include/linux/phy/lynx-10g.h
> +
> LYNX 28G SERDES PHY DRIVER
> M: Ioana Ciornei <ioana.ciornei@nxp.com>
> L: netdev@vger.kernel.org
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index e3ca0d058a25..eebed69f6c58 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -33,6 +33,7 @@ obj-$(CONFIG_ARCH_SPARX5) += clk-sparx5.o
> obj-$(CONFIG_COMMON_CLK_EN7523) += clk-en7523.o
> obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o
> obj-$(CONFIG_COMMON_CLK_FSL_FLEXSPI) += clk-fsl-flexspi.o
> +obj-$(CONFIG_PHY_FSL_LYNX_10G) += clk-fsl-lynx-10g.o
> obj-$(CONFIG_COMMON_CLK_FSL_SAI) += clk-fsl-sai.o
> obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o
> obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o
> diff --git a/drivers/clk/clk-fsl-lynx-10g.c b/drivers/clk/clk-fsl-lynx-10g.c
> new file mode 100644
> index 000000000000..78357303b578
> --- /dev/null
> +++ b/drivers/clk/clk-fsl-lynx-10g.c
> @@ -0,0 +1,510 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
> + *
> + * This file contains the implementation for the PLLs found on Lynx 10G phys.
> + *
> + * XXX: The VCO rate of the PLLs can exceed ~4GHz, which is the maximum rate
> + * expressable in an unsigned long. To work around this, rates are specified in
> + * kHz. This is as if there was a division by 1000 in the PLL.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/bitfield.h>
> +#include <linux/math64.h>
> +#include <linux/phy/lynx-10g.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/units.h>
> +#include <dt-bindings/clock/fsl,lynx-10g.h>
> +
> +#define PLL_STRIDE 0x20
> +#define PLLa(a, off) ((a) * PLL_STRIDE + (off))
> +#define PLLaRSTCTL(a) PLLa(a, 0x00)
> +#define PLLaCR0(a) PLLa(a, 0x04)
> +
> +#define PLLaRSTCTL_RSTREQ BIT(31)
> +#define PLLaRSTCTL_RST_DONE BIT(30)
> +#define PLLaRSTCTL_RST_ERR BIT(29)
> +#define PLLaRSTCTL_PLLRST_B BIT(7)
> +#define PLLaRSTCTL_SDRST_B BIT(6)
> +#define PLLaRSTCTL_SDEN BIT(5)
> +
> +#define PLLaRSTCTL_ENABLE_SET (PLLaRSTCTL_RST_DONE | PLLaRSTCTL_PLLRST_B | \
> + PLLaRSTCTL_SDRST_B | PLLaRSTCTL_SDEN)
> +#define PLLaRSTCTL_ENABLE_MASK (PLLaRSTCTL_ENABLE_SET | PLLaRSTCTL_RST_ERR)
> +
> +#define PLLaCR0_POFF BIT(31)
> +#define PLLaCR0_RFCLK_SEL GENMASK(30, 28)
> +#define PLLaCR0_PLL_LCK BIT(23)
> +#define PLLaCR0_FRATE_SEL GENMASK(19, 16)
> +#define PLLaCR0_DLYDIV_SEL GENMASK(1, 0)
> +
> +#define PLLaCR0_DLYDIV_SEL_16 0b01
> +
> +/**
> + * struct lynx_clk - Driver data for the PLLs
> + * @pll: The PLL clock
> + * @ex_dly: The "PLLa_ex_dly_clk" clock
> + * @dev: The serdes device
> + * @regmap: Our registers
> + * @idx: Which PLL this clock is for
> + */
> +struct lynx_clk {
> + struct clk_hw pll, ex_dly;
> + struct device *dev;
> + struct regmap *regmap;
> + unsigned int idx;
> +};
> +
> +static u32 lynx_read(struct lynx_clk *clk, u32 reg)
> +{
> + unsigned int ret = 0;
> +
> + WARN_ON_ONCE(regmap_read(clk->regmap, reg, &ret));
> + return ret;
> +}
> +
> +static void lynx_write(struct lynx_clk *clk, u32 val, u32 reg)
> +{
> + WARN_ON_ONCE(regmap_write(clk->regmap, reg, val));
> +}
> +
> +static struct lynx_clk *lynx_pll_to_clk(struct clk_hw *hw)
> +{
> + return container_of(hw, struct lynx_clk, pll);
> +}
> +
> +static struct lynx_clk *lynx_ex_dly_to_clk(struct clk_hw *hw)
> +{
> + return container_of(hw, struct lynx_clk, ex_dly);
> +}
> +
> +static void lynx_pll_stop(struct lynx_clk *clk)
> +{
> + u32 rstctl;
> +
> + rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
> + rstctl &= ~PLLaRSTCTL_SDRST_B;
> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
> +
> + ndelay(50);
> +
> + rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
> + rstctl &= ~(PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B);
> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
> +
> + ndelay(100);
> +}
> +
> +static void lynx_pll_disable(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 cr0;
> +
> + dev_dbg(clk->dev, "disable pll%d\n", clk->idx);
> +
> + lynx_pll_stop(clk);
> +
> + cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> + cr0 |= PLLaCR0_POFF;
> + lynx_write(clk, cr0, PLLaCR0(clk->idx));
> +}
> +
> +static int lynx_pll_reset(struct lynx_clk *clk)
> +{
> + int ret;
> + u32 rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
> +
> + rstctl |= PLLaRSTCTL_RSTREQ;
> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
> + ret = read_poll_timeout(lynx_read, rstctl,
> + rstctl & (PLLaRSTCTL_RST_DONE | PLLaRSTCTL_RST_ERR),
> + 100, 5000, true, clk, PLLaRSTCTL(clk->idx));
> + if (rstctl & PLLaRSTCTL_RST_ERR)
> + ret = -EIO;
> + if (ret) {
> + dev_err(clk->dev, "pll%d reset failed\n", clk->idx);
> + return ret;
> + }
> +
> + rstctl |= PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B | PLLaRSTCTL_SDRST_B;
> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
> + return 0;
> +}
> +
> +static int lynx_pll_prepare(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> +
> + /*
> + * "Enabling" the PLL involves resetting it (and all attached lanes).
> + * Avoid doing this if we are already enabled.
> + */
> + if (!(cr0 & PLLaCR0_POFF) &&
> + (rstctl & PLLaRSTCTL_ENABLE_MASK) == PLLaRSTCTL_ENABLE_SET) {
> + dev_dbg(clk->dev, "pll%d already prepared\n", clk->idx);
> + return 0;
> + }
> +
> + dev_dbg(clk->dev, "prepare pll%d\n", clk->idx);
> +
> + cr0 &= ~PLLaCR0_POFF;
> + lynx_write(clk, cr0, PLLaCR0(clk->idx));
> +
> + return lynx_pll_reset(clk);
> +}
> +
> +static int lynx_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> + bool enabled = !(cr0 & PLLaCR0_POFF);
> +
> + dev_dbg(clk->dev, "pll%d %s enabled\n", clk->idx,
> + enabled ? "is" : "is not");
> +
> + return enabled;
> +}
> +
> +static const unsigned long rfclk_sel_map[8] = {
> + [0b000] = 100000000,
> + [0b001] = 125000000,
> + [0b010] = 156250000,
> + [0b011] = 150000000,
> +};
> +
> +/**
> + * lynx_rfclk_to_sel() - Convert a reference clock rate to a selector
> + * @rate: The reference clock rate
> + *
> + * To allow for some variation in the reference clock rate, up to 100ppm of
> + * error is allowed.
> + *
> + * Return: An appropriate selector for @rate, or -%EINVAL.
> + */
> +static int lynx_rfclk_to_sel(unsigned long rate)
> +{
> + int ret;
> +
> + for (ret = 0; ret < ARRAY_SIZE(rfclk_sel_map); ret++) {
> + unsigned long rfclk_rate = rfclk_sel_map[ret];
> + /* Allow an error of 100ppm */
> + unsigned long error = rfclk_rate / 10000;
> +
> + if (abs(rate - rfclk_rate) < error)
> + return ret;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const unsigned long frate_sel_map[16] = {
> + [0b0000] = 5000000,
> + [0b0101] = 3750000,
> + [0b0110] = 5156250,
> + [0b0111] = 4000000,
> + [0b1001] = 3125000,
> + [0b1010] = 3000000,
> +};
> +
> +/**
> + * lynx_frate_to_sel() - Convert a VCO clock rate to a selector
> + * @rate_khz: The VCO frequency, in kHz
> + *
> + * Return: An appropriate selector for @rate_khz, or -%EINVAL.
> + */
> +static int lynx_frate_to_sel(unsigned long rate_khz)
> +{
> + int ret;
> +
> + for (ret = 0; ret < ARRAY_SIZE(frate_sel_map); ret++)
> + if (frate_sel_map[ret] == rate_khz)
> + return ret;
> +
> + return -EINVAL;
> +}
> +
> +static u32 lynx_pll_ratio(u32 frate_sel, u32 rfclk_sel)
> +{
> + u64 frate;
> + u32 rfclk, error, ratio;
> +
> + frate = frate_sel_map[frate_sel] * (u64)HZ_PER_KHZ;
> + rfclk = rfclk_sel_map[rfclk_sel];
> +
> + if (!frate || !rfclk)
> + return 0;
> +
> + ratio = div_u64_rem(frate, rfclk, &error);
> + if (!error)
> + return ratio;
> + return 0;
> +}
> +
> +static unsigned long lynx_pll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> + u32 frate_sel = FIELD_GET(PLLaCR0_FRATE_SEL, cr0);
> + u32 rfclk_sel = FIELD_GET(PLLaCR0_RFCLK_SEL, cr0);
> + u32 ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> + unsigned long ret;
> +
> + /* Ensure that the parent matches our rfclk selector */
> + if (rfclk_sel == lynx_rfclk_to_sel(parent_rate))
> + ret = mult_frac(parent_rate, ratio, HZ_PER_KHZ);
> + else
> + ret = 0;
> +
> + dev_dbg(clk->dev, "recalc pll%d new=%llu parent=%lu\n", clk->idx,
> + (u64)ret * HZ_PER_KHZ, parent_rate);
> + return ret;
> +}
> +
> +static int lynx_pll_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
> + int frate_sel, rfclk_sel;
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 ratio;
> +
> + dev_dbg(clk->dev, "round pll%d new=%llu parent=%lu\n", clk->idx,
> + (u64)req->rate * HZ_PER_KHZ, req->best_parent_rate);
> +
> + frate_sel = lynx_frate_to_sel(req->rate);
> + if (frate_sel < 0)
> + return frate_sel;
> +
> + /* Try the current parent rate */
> + rfclk_sel = lynx_rfclk_to_sel(req->best_parent_rate);
> + if (rfclk_sel >= 0) {
> + ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> + if (ratio) {
> + req->rate = mult_frac(req->best_parent_rate, ratio,
> + HZ_PER_KHZ);
> + return 0;
> + }
> + }
> +
> + /* Try all possible parent rates */
> + for (rfclk_sel = 0;
> + rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
> + rfclk_sel++) {
> + unsigned long new_parent_rate;
> +
> + ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> + if (!ratio)
> + continue;
> +
> + /* Ensure the reference clock can produce this rate */
> + new_parent_rate = rfclk_sel_map[rfclk_sel];
> + new_parent_rate = clk_hw_round_rate(req->best_parent_hw,
> + new_parent_rate);
> + if (rfclk_sel != lynx_rfclk_to_sel(new_parent_rate))
> + continue;
> +
> + req->rate = mult_frac(new_parent_rate, ratio, HZ_PER_KHZ);
> + req->best_parent_rate = new_parent_rate;
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int lynx_pll_set_rate(struct clk_hw *hw, unsigned long rate_khz,
> + unsigned long parent_rate)
> +{
> + int frate_sel, rfclk_sel;
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 ratio, cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> +
> + dev_dbg(clk->dev, "set rate pll%d new=%llu parent=%lu\n", clk->idx,
> + (u64)rate_khz * HZ_PER_KHZ, parent_rate);
> +
> + frate_sel = lynx_frate_to_sel(rate_khz);
> + if (frate_sel < 0)
> + return frate_sel;
> +
> + rfclk_sel = lynx_rfclk_to_sel(parent_rate);
> + if (rfclk_sel < 0)
> + return rfclk_sel;
> +
> + ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> + if (!ratio)
> + return -EINVAL;
> +
> + lynx_pll_stop(clk);
> + cr0 &= ~(PLLaCR0_RFCLK_SEL | PLLaCR0_FRATE_SEL);
> + cr0 |= FIELD_PREP(PLLaCR0_RFCLK_SEL, rfclk_sel);
> + cr0 |= FIELD_PREP(PLLaCR0_FRATE_SEL, frate_sel);
> + lynx_write(clk, cr0, PLLaCR0(clk->idx));
> + /* Don't bother resetting if it's off */
> + if (cr0 & PLLaCR0_POFF)
> + return 0;
> + return lynx_pll_reset(clk);
> +}
> +
> +static const struct clk_ops lynx_pll_clk_ops = {
> + .prepare = lynx_pll_prepare,
> + .disable = lynx_pll_disable,
> + .is_enabled = lynx_pll_is_enabled,
> + .recalc_rate = lynx_pll_recalc_rate,
> + .determine_rate = lynx_pll_determine_rate,
> + .set_rate = lynx_pll_set_rate,
> +};
> +
> +static void lynx_ex_dly_disable(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> +
> + cr0 &= ~PLLaCR0_DLYDIV_SEL;
> + lynx_write(clk, PLLaCR0(clk->idx), cr0);
> +}
> +
> +static int lynx_ex_dly_enable(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> +
> + cr0 &= ~PLLaCR0_DLYDIV_SEL;
> + cr0 |= FIELD_PREP(PLLaCR0_DLYDIV_SEL, PLLaCR0_DLYDIV_SEL_16);
> + lynx_write(clk, PLLaCR0(clk->idx), cr0);
> + return 0;
> +}
> +
> +static int lynx_ex_dly_is_enabled(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
> +
> + return lynx_read(clk, PLLaCR0(clk->idx)) & PLLaCR0_DLYDIV_SEL;
> +}
> +
> +static unsigned long lynx_ex_dly_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + return parent_rate / 16;
> +}
> +
> +static const struct clk_ops lynx_ex_dly_clk_ops = {
> + .enable = lynx_ex_dly_enable,
> + .disable = lynx_ex_dly_disable,
> + .is_enabled = lynx_ex_dly_is_enabled,
> + .recalc_rate = lynx_ex_dly_recalc_rate,
> +};
> +
> +static int lynx_clk_init(struct clk_hw_onecell_data *hw_data,
> + struct device *dev, struct regmap *regmap,
> + unsigned int index, bool compat)
> +{
> + const struct clk_hw *ex_dly_parents;
> + struct clk_parent_data pll_parents[1] = { };
> + struct clk_init_data pll_init = {
> + .ops = &lynx_pll_clk_ops,
> + .parent_data = pll_parents,
> + .num_parents = 1,
> + .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT |
> + CLK_OPS_PARENT_ENABLE,
> + };
> + struct clk_init_data ex_dly_init = {
> + .ops = &lynx_ex_dly_clk_ops,
> + .parent_hws = &ex_dly_parents,
> + .num_parents = 1,
> + };
> + struct lynx_clk *clk;
> + int ret;
> +
> + clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
> + if (!clk)
> + return -ENOMEM;
> +
> + clk->dev = dev;
> + clk->regmap = regmap;
> + clk->idx = index;
> +
> + pll_parents[0].fw_name = kasprintf(GFP_KERNEL, "ref%d", index);
> + pll_init.name = kasprintf(GFP_KERNEL, "%s.pll%d_khz", dev_name(dev),
> + index);
> + ex_dly_init.name = kasprintf(GFP_KERNEL, "%s.pll%d_ex_dly_khz",
> + dev_name(dev), index);
> + if (!pll_parents[0].fw_name || !pll_init.name || !ex_dly_init.name) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + if (compat)
> + pll_init.flags |= CLK_IGNORE_UNUSED;
> + clk->pll.init = &pll_init;
> + ret = devm_clk_hw_register(dev, &clk->pll);
> + if (ret) {
> + dev_err_probe(dev, ret, "could not register %s\n",
> + pll_init.name);
> + goto out;
> + }
> +
> + ex_dly_parents = &clk->pll;
> + clk->ex_dly.init = &ex_dly_init;
> + ret = devm_clk_hw_register(dev, &clk->ex_dly);
> + if (ret)
> + dev_err_probe(dev, ret, "could not register %s\n",
> + ex_dly_init.name);
> +
> + hw_data->hws[LYNX10G_PLLa(index)] = &clk->pll;
> + hw_data->hws[LYNX10G_PLLa_EX_DLY(index)] = &clk->ex_dly;
> +
> +out:
> + kfree(pll_parents[0].fw_name);
> + kfree(pll_init.name);
> + kfree(ex_dly_init.name);
> + return ret;
> +}
> +
> +#define NUM_PLLS 2
> +#define NUM_CLKS (NUM_PLLS * LYNX10G_CLKS_PER_PLL)
> +
> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat)
> +{
> + int ret, i;
> + struct clk_hw_onecell_data *hw_data;
> +
> + hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, NUM_CLKS),
> + GFP_KERNEL);
> + if (!hw_data)
> + return -ENOMEM;
> + hw_data->num = NUM_CLKS;
> +
> + for (i = 0; i < NUM_PLLS; i++) {
> + ret = lynx_clk_init(hw_data, dev, regmap, i, compat);
> + if (ret)
> + return ret;
> +
> + plls[i] = devm_clk_hw_get_clk(dev,
> + hw_data->hws[LYNX10G_PLLa(i)],
> + NULL);
> + if (IS_ERR(plls[i]))
> + return PTR_ERR(plls[i]);
> +
> + ex_dlys[i] = devm_clk_hw_get_clk(dev,
> + hw_data->hws[LYNX10G_PLLa_EX_DLY(i)],
> + NULL);
> + if (IS_ERR(ex_dlys[i]))
> + return PTR_ERR(plls[i]);
> + }
> +
> + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
> + if (ret)
> + dev_err_probe(dev, ret, "could not register clock provider\n");
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(lynx_clks_init);
> +
> +MODULE_AUTHOR("Sean Anderson <sean.anderson@seco.com>");
> +MODULE_DESCRIPTION("Lynx 10G PLL driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
> index 853958fb2c06..5d461232276f 100644
> --- a/drivers/phy/freescale/Kconfig
> +++ b/drivers/phy/freescale/Kconfig
> @@ -47,3 +47,9 @@ config PHY_FSL_LYNX_28G
> found on NXP's Layerscape platforms such as LX2160A.
> Used to change the protocol running on SerDes lanes at runtime.
> Only useful for a restricted set of Ethernet protocols.
> +
> +config PHY_FSL_LYNX_10G
> + tristate
> + depends on COMMON_CLK
> + depends on ARCH_LAYERSCAPE || PPC || COMPILE_TEST
> + select REGMAP_MMIO
Why is this change in clk driver part?
> diff --git a/include/linux/phy/lynx-10g.h b/include/linux/phy/lynx-10g.h
> new file mode 100644
> index 000000000000..b7b80b3ee988
> --- /dev/null
> +++ b/include/linux/phy/lynx-10g.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
> + */
> +
> +#ifndef LYNX_10G
> +#define LYNX_10G
> +
> +struct clk;
> +struct device;
> +struct regmap;
> +
> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat);
so you have an exported symbol for clk driver init in phy driver header?
can you please explain why..?
> +
> +#endif /* LYNX 10G */
> --
> 2.35.1.1320.gc452695387.dirty
--
~Vinod
next prev parent reply other threads:[~2023-05-08 9:15 UTC|newest]
Thread overview: 64+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
2023-04-13 16:05 ` [PATCH v14 01/15] dt-bindings: phy: Add 2500BASE-X and 10GBASE-R Sean Anderson
2023-04-13 16:05 ` [PATCH v14 02/15] dt-bindings: phy: Add Lynx 10G phy binding Sean Anderson
2023-04-13 16:05 ` [PATCH v14 03/15] dt-bindings: Convert gpio-mmio to yaml Sean Anderson
2023-04-18 20:37 ` Rob Herring
2023-05-11 9:18 ` Bartosz Golaszewski
2023-04-13 16:05 ` [PATCH v14 04/15] dt-bindings: gpio-mmio: Add compatible for QIXIS Sean Anderson
2023-04-13 16:05 ` [PATCH v14 05/15] dt-bindings: clock: Add ids for Lynx 10g PLLs Sean Anderson
2023-04-13 16:05 ` [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver Sean Anderson
2023-05-08 9:15 ` Vinod Koul [this message]
2023-05-08 15:31 ` Sean Anderson
2023-05-09 13:00 ` Vinod Koul
2023-05-09 15:26 ` Sean Anderson
2023-05-16 13:22 ` Vinod Koul
2023-05-16 15:11 ` Sean Anderson
2023-05-16 16:32 ` Vinod Koul
2023-04-13 16:05 ` [PATCH v14 07/15] phy: fsl: Add Lynx 10G SerDes driver Sean Anderson
2023-05-08 9:22 ` Vinod Koul
2023-05-08 15:28 ` Sean Anderson
2023-05-16 13:36 ` Vinod Koul
2023-05-16 15:12 ` Sean Anderson
2023-04-13 16:06 ` [PATCH v14 08/15] phy: lynx10g: Enable by default on Layerscape Sean Anderson
2023-04-13 16:06 ` [PATCH v14 09/15] arm64: dts: ls1046a: Add serdes nodes Sean Anderson
2023-04-13 16:06 ` [PATCH v14 10/15] arm64: dts: ls1046ardb: Add serdes descriptions Sean Anderson
2023-04-13 16:06 ` [PATCH v14 11/15] arm64: dts: ls1088a: Add serdes nodes Sean Anderson
2023-04-13 16:06 ` [PATCH v14 12/15] arm64: dts: ls1088a: Prevent PCSs from probing as phys Sean Anderson
2023-04-13 16:06 ` [PATCH v14 13/15] arm64: dts: ls1088ardb: Remove aquantia interrupt Sean Anderson
2023-04-13 16:06 ` [PATCH v14 14/15] arm64: dts: ls1088ardb: Add SFP cage Sean Anderson
2023-04-13 16:06 ` [PATCH v14 15/15] arm64: dts: ls1088ardb: Add serdes descriptions Sean Anderson
2023-04-25 19:50 ` [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Vladimir Oltean
2023-04-25 20:22 ` Sean Anderson
2023-04-26 10:51 ` Vladimir Oltean
2023-04-26 14:50 ` Sean Anderson
2023-04-29 17:24 ` Vladimir Oltean
2023-05-01 15:03 ` Sean Anderson
2023-05-22 14:42 ` Sean Anderson
2023-05-22 15:00 ` Vladimir Oltean
2023-06-09 19:19 ` Sean Anderson
2023-06-10 22:21 ` Vladimir Oltean
2023-06-12 14:35 ` Sean Anderson
2023-06-12 16:33 ` Vladimir Oltean
2023-06-12 20:46 ` Sean Anderson
2023-06-13 14:27 ` Vladimir Oltean
2023-08-10 10:26 ` Vladimir Oltean
2023-08-10 19:58 ` Sean Anderson
2023-08-11 16:12 ` Vladimir Oltean
2023-09-13 22:02 ` Vladimir Oltean
2023-08-11 15:08 ` Vladimir Oltean
2023-08-11 15:43 ` Sean Anderson
2023-08-11 16:36 ` Vladimir Oltean
2023-08-21 12:49 ` Vladimir Oltean
2023-08-21 17:45 ` Sean Anderson
2023-08-21 18:13 ` Ioana Ciornei
2023-08-21 18:20 ` Vladimir Oltean
2023-08-21 18:46 ` Sean Anderson
2023-08-21 19:58 ` Vladimir Oltean
2023-08-21 21:06 ` Sean Anderson
2023-08-21 22:48 ` Vladimir Oltean
2023-08-21 23:39 ` Sean Anderson
2023-08-21 23:59 ` Vladimir Oltean
2023-08-24 22:09 ` Sean Anderson
2023-08-25 14:43 ` Vladimir Oltean
2023-08-22 14:55 ` Ioana Ciornei
2023-08-24 20:54 ` Sean Anderson
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=ZFi9t84UoIfUyHhi@matsya \
--to=vkoul@kernel.org \
--cc=bagasdotme@gmail.com \
--cc=camelia.groza@nxp.com \
--cc=devicetree@vger.kernel.org \
--cc=ioana.ciornei@nxp.com \
--cc=kishon@kernel.org \
--cc=krzysztof.kozlowski+dt@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-phy@lists.infradead.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=madalin.bucur@nxp.com \
--cc=mturquette@baylibre.com \
--cc=robh+dt@kernel.org \
--cc=sboyd@kernel.org \
--cc=sean.anderson@seco.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).