From: Stephen Boyd <sboyd@kernel.org>
To: Li Yang <leoyang.li@nxp.com>, Mark Rutland <mark.rutland@arm.com>,
Michael Turquette <mturquette@baylibre.com>,
Michael Walle <michael@walle.cc>,
Rob Herring <robh+dt@kernel.org>, Wen He <wen.he_1@nxp.com>,
devicetree@vger.kernel.org, linux-clk@vger.kernel.org,
linux-kernel@vger.kernel.org
Cc: Wen He <wen.he_1@nxp.com>
Subject: Re: [v11 2/2] clk: ls1028a: Add clock driver for Display output interface
Date: Thu, 12 Dec 2019 14:18:16 -0800 [thread overview]
Message-ID: <20191212221817.B7FF1206DA@mail.kernel.org> (raw)
In-Reply-To: <20191205072653.34701-2-wen.he_1@nxp.com>
Quoting Wen He (2019-12-04 23:26:53)
> Add clock driver for QorIQ LS1028A Display output interfaces(LCD, DPHY),
> as implemented in TSMC CLN28HPM PLL, this PLL supports the programmable
> integer division and range of the display output pixel clock's 27-594MHz.
>
> Signed-off-by: Wen He <wen.he_1@nxp.com>
> Signed-off-by: Michael Walle <michael@walle.cc>
Is Michael the author? SoB chain is backwards here.
> diff --git a/drivers/clk/clk-plldig.c b/drivers/clk/clk-plldig.c
> new file mode 100644
> index 000000000000..1942686f0254
> --- /dev/null
> +++ b/drivers/clk/clk-plldig.c
> @@ -0,0 +1,297 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 NXP
> + *
> + * Clock driver for LS1028A Display output interfaces(LCD, DPHY).
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/bitfield.h>
> +
> +/* PLLDIG register offsets and bit masks */
> +#define PLLDIG_REG_PLLSR 0x24
> +#define PLLDIG_LOCK_MASK BIT(2)
> +#define PLLDIG_REG_PLLDV 0x28
> +#define PLLDIG_MFD_MASK GENMASK(7, 0)
> +#define PLLDIG_RFDPHI1_MASK GENMASK(30, 25)
> +#define PLLDIG_REG_PLLFM 0x2c
> +#define PLLDIG_SSCGBYP_ENABLE BIT(30)
> +#define PLLDIG_REG_PLLFD 0x30
> +#define PLLDIG_FDEN BIT(30)
> +#define PLLDIG_FRAC_MASK GENMASK(15, 0)
> +#define PLLDIG_REG_PLLCAL1 0x38
> +#define PLLDIG_REG_PLLCAL2 0x3c
> +
> +/* Range of the VCO frequencies, in Hz */
> +#define PLLDIG_MIN_VCO_FREQ 650000000
> +#define PLLDIG_MAX_VCO_FREQ 1300000000
> +
> +/* Range of the output frequencies, in Hz */
> +#define PHI1_MIN_FREQ 27000000
> +#define PHI1_MAX_FREQ 600000000
> +
> +/* Maximum value of the reduced frequency divider */
> +#define MAX_RFDPHI1 63UL
> +
> +/* Best value of multiplication factor divider */
> +#define PLLDIG_DEFAULT_MFD 44
> +
> +/*
> + * Denominator part of the fractional part of the
> + * loop multiplication factor.
> + */
> +#define MFDEN 20480
> +
> +static const struct clk_parent_data parent_data[] = {
> + {.index = 0},
Nitpick: Add spaces after { and before }
> +};
> +
> +struct clk_plldig {
> + struct clk_hw hw;
> + void __iomem *regs;
> + unsigned int vco_freq;
> +};
> +
> +#define to_clk_plldig(_hw) container_of(_hw, struct clk_plldig, hw)
> +
> +static int plldig_enable(struct clk_hw *hw)
> +{
> + struct clk_plldig *data = to_clk_plldig(hw);
> + u32 val;
> +
> + val = readl(data->regs + PLLDIG_REG_PLLFM);
> + /*
> + * Use Bypass mode with PLL off by default, the frequency overshoot
> + * detector output was disable. SSCG Bypass mode should be enable.
> + */
> + val |= PLLDIG_SSCGBYP_ENABLE;
> + writel(val, data->regs + PLLDIG_REG_PLLFM);
> +
> + return 0;
> +}
> +
> +static void plldig_disable(struct clk_hw *hw)
> +{
> + struct clk_plldig *data = to_clk_plldig(hw);
> + u32 val;
> +
> + val = readl(data->regs + PLLDIG_REG_PLLFM);
> +
> + val &= ~PLLDIG_SSCGBYP_ENABLE;
> + val |= FIELD_PREP(PLLDIG_SSCGBYP_ENABLE, 0x0);
> +
> + writel(val, data->regs + PLLDIG_REG_PLLFM);
> +}
> +
> +static int plldig_is_enabled(struct clk_hw *hw)
> +{
> + struct clk_plldig *data = to_clk_plldig(hw);
> +
> + return (readl(data->regs + PLLDIG_REG_PLLFM) &
Please drop useless parenthesis.
> + PLLDIG_SSCGBYP_ENABLE);
> +}
> +
> +static unsigned long plldig_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct clk_plldig *data = to_clk_plldig(hw);
> + u32 val, rfdphi1;
> +
> + val = readl(data->regs + PLLDIG_REG_PLLDV);
> +
> + /* Check if PLL is bypassed */
> + if (val & PLLDIG_SSCGBYP_ENABLE)
> + return parent_rate;
> +
> + rfdphi1 = FIELD_GET(PLLDIG_RFDPHI1_MASK, val);
> +
> + /*
> + * If RFDPHI1 has a value of 1 the VCO frequency is also divided by
> + * one.
> + */
> + if (!rfdphi1)
> + rfdphi1 = 1;
> +
> + return DIV_ROUND_UP(data->vco_freq, rfdphi1);
> +}
> +
> +static unsigned long plldig_calc_target_div(unsigned long vco_freq,
> + unsigned long target_rate)
> +{
> + unsigned long div;
> +
> + div = DIV_ROUND_CLOSEST(vco_freq, target_rate);
> + div = max(1UL, div);
> + div = min(div, MAX_RFDPHI1);
Use clamp().
> +
> + return div;
> +}
> +
> +static int plldig_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
> + struct clk_plldig *data = to_clk_plldig(hw);
> + unsigned int div;
> +
> + if (req->rate < PHI1_MIN_FREQ)
> + req->rate = PHI1_MIN_FREQ;
> + if (req->rate > PHI1_MAX_FREQ)
> + req->rate = PHI1_MAX_FREQ;
Use clamp()
> +
> + div = plldig_calc_target_div(data->vco_freq, req->rate);
> + req->rate = DIV_ROUND_UP(data->vco_freq, div);
> +
> + return 0;
> +}
> +
> +static int plldig_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct clk_plldig *data = to_clk_plldig(hw);
> + unsigned int val, cond;
> + unsigned int rfdphi1;
> +
> + if (rate < PHI1_MIN_FREQ)
> + rate = PHI1_MIN_FREQ;
> + if (rate > PHI1_MAX_FREQ)
> + rate = PHI1_MAX_FREQ;
Use clamp()
> +
> + rfdphi1 = plldig_calc_target_div(data->vco_freq, rate);
> +
> + /* update the divider value */
> + val = readl(data->regs + PLLDIG_REG_PLLDV);
> + val &= ~PLLDIG_RFDPHI1_MASK;
> + val |= FIELD_PREP(PLLDIG_RFDPHI1_MASK, rfdphi1);
> + writel(val, data->regs + PLLDIG_REG_PLLDV);
> +
> + /* delay 200us make sure that old lock state is cleared */
> + udelay(200);
Please remove 'delay 200us' from the comment. Just say that we're waiting
for old lock state to clear. It's clear from the code how much time it
is.
> +
> + /* Wait until PLL is locked or timeout (maximum 1000 usecs) */
Drop the time. It's a millisecond.
> + return readl_poll_timeout_atomic(data->regs + PLLDIG_REG_PLLSR, cond,
> + cond & PLLDIG_LOCK_MASK, 0,
> + USEC_PER_MSEC);
> +}
> +
> +static const struct clk_ops plldig_clk_ops = {
> + .enable = plldig_enable,
> + .disable = plldig_disable,
> + .is_enabled = plldig_is_enabled,
> + .recalc_rate = plldig_recalc_rate,
> + .determine_rate = plldig_determine_rate,
> + .set_rate = plldig_set_rate,
> +};
> +
> +static int plldig_init(struct clk_hw *hw)
> +{
> + struct clk_plldig *data = to_clk_plldig(hw);
> + struct clk_hw *parent = clk_hw_get_parent(hw);
> + unsigned long parent_rate = clk_hw_get_rate(parent);
> + unsigned long val;
> + unsigned long long lltmp;
> + unsigned int mfd, fracdiv = 0;
> +
> + if (!parent)
> + return -EINVAL;
> +
> + if (data->vco_freq) {
> + mfd = data->vco_freq / parent_rate;
> + lltmp = data->vco_freq % parent_rate;
> + lltmp *= MFDEN;
> + do_div(lltmp, parent_rate);
> + fracdiv = lltmp;
> + } else {
> + mfd = PLLDIG_DEFAULT_MFD;
> + data->vco_freq = parent_rate * mfd;
> + }
> +
> + val = FIELD_PREP(PLLDIG_MFD_MASK, mfd);
> + writel(val, data->regs + PLLDIG_REG_PLLDV);
> +
> + if (fracdiv) {
> + val = FIELD_PREP(PLLDIG_FRAC_MASK, fracdiv);
> + /* Enable fractional divider */
Remove useless comment please. Or move above the if condition.
> + val |= PLLDIG_FDEN;
> + writel(val, data->regs + PLLDIG_REG_PLLFD);
> + }
> +
> + return 0;
> +}
> +
> +static int plldig_clk_probe(struct platform_device *pdev)
> +{
> + struct clk_plldig *data;
> + struct resource *mem;
> + struct device *dev = &pdev->dev;
> + int ret;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + data->regs = devm_ioremap_resource(dev, mem);
We have devm_platform_ioremap_resource() for this now.
> + if (IS_ERR(data->regs))
> + return PTR_ERR(data->regs);
> +
> + data->hw.init = CLK_HW_INIT_PARENTS_DATA("dpclk",
> + parent_data,
> + &plldig_clk_ops,
> + 0);
> +
> + ret = devm_clk_hw_register(dev, &data->hw);
> + if (ret) {
> + dev_err(dev, "failed to register %s clock\n",
> + dev->of_node->name);
> + return ret;
> + }
> +
> + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
> + &data->hw);
> + if (ret) {
> + dev_err(dev, "unable to add clk provider\n");
> + return ret;
> + }
> +
> + /*
> + * The frequency of the VCO cannot be changed during runtime.
> + * Therefore, let the user specify a desired frequency.
> + */
> + if (!of_property_read_u32(dev->of_node, "fsl,vco-hz",
> + &data->vco_freq)) {
> + if (data->vco_freq < PLLDIG_MIN_VCO_FREQ ||
> + data->vco_freq > PLLDIG_MAX_VCO_FREQ)
> + return -EINVAL;
> + }
> +
> + return plldig_init(&data->hw);
> +}
> +
> +static const struct of_device_id plldig_clk_id[] = {
> + { .compatible = "fsl,ls1028a-plldig"},
Nitpick: Add a space before }
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, plldig_clk_id);
> +
next prev parent reply other threads:[~2019-12-12 22:18 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-12-05 7:26 [v11 1/2] dt/bindings: clk: Add YAML schemas for LS1028A Display Clock bindings Wen He
2019-12-05 7:26 ` [v11 2/2] clk: ls1028a: Add clock driver for Display output interface Wen He
2019-12-09 9:13 ` Wen He
2019-12-12 22:18 ` Stephen Boyd [this message]
2019-12-13 0:06 ` Michael Walle
2019-12-16 17:55 ` Stephen Boyd
2019-12-17 7:08 ` [EXT] " Wen He
2019-12-20 14:42 ` Michael Walle
2019-12-05 14:26 ` [v11 1/2] dt/bindings: clk: Add YAML schemas for LS1028A Display Clock bindings Rob Herring
2019-12-06 3:01 ` [EXT] " Wen He
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=20191212221817.B7FF1206DA@mail.kernel.org \
--to=sboyd@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=leoyang.li@nxp.com \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=michael@walle.cc \
--cc=mturquette@baylibre.com \
--cc=robh+dt@kernel.org \
--cc=wen.he_1@nxp.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.