From: mturquette@linaro.org (Mike Turquette)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH resend] clk: axi-clkgen: Add support for v2
Date: Wed, 26 Feb 2014 17:04:21 -0800 [thread overview]
Message-ID: <20140227010421.12081.82707@quantum> (raw)
In-Reply-To: <1392629513-6114-1-git-send-email-lars@metafoo.de>
Quoting Lars-Peter Clausen (2014-02-17 01:31:53)
> This patch adds support for the new v2 version of the axi-clkgen core.
> Unfortunately the method of accessing the registers is quite different on v2,
> while the content still stays largely the same. So the patch adds a small
> abstraction layer which implements the specific read and write functions for v1
> and v2 in callback functions.
Hi,
This patch almost doubles the size of clk-axi-clkgen.c. Should it be a
separate clock driver? I guess that depends on the relationship between
"v1" and "v2". Are both of those versions of the clkgen core going into
production?
Regards,
Mike
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> ---
> .../devicetree/bindings/clock/axi-clkgen.txt | 2 +-
> drivers/clk/clk-axi-clkgen.c | 312 ++++++++++++++++++---
> 2 files changed, 270 insertions(+), 44 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/clock/axi-clkgen.txt b/Documentation/devicetree/bindings/clock/axi-clkgen.txt
> index 028b493..20e1704 100644
> --- a/Documentation/devicetree/bindings/clock/axi-clkgen.txt
> +++ b/Documentation/devicetree/bindings/clock/axi-clkgen.txt
> @@ -5,7 +5,7 @@ This binding uses the common clock binding[1].
> [1] Documentation/devicetree/bindings/clock/clock-bindings.txt
>
> Required properties:
> -- compatible : shall be "adi,axi-clkgen".
> +- compatible : shall be "adi,axi-clkgen-1.00.a" or "adi,axi-clkgen-2.00.a".
> - #clock-cells : from common clock binding; Should always be set to 0.
> - reg : Address and length of the axi-clkgen register set.
> - clocks : Phandle and clock specifier for the parent clock.
> diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
> index 8137327..1127ee4 100644
> --- a/drivers/clk/clk-axi-clkgen.c
> +++ b/drivers/clk/clk-axi-clkgen.c
> @@ -17,23 +17,75 @@
> #include <linux/module.h>
> #include <linux/err.h>
>
> -#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04
> -#define AXI_CLKGEN_REG_CLK_OUT1 0x08
> -#define AXI_CLKGEN_REG_CLK_OUT2 0x0c
> -#define AXI_CLKGEN_REG_CLK_DIV 0x10
> -#define AXI_CLKGEN_REG_CLK_FB1 0x14
> -#define AXI_CLKGEN_REG_CLK_FB2 0x18
> -#define AXI_CLKGEN_REG_LOCK1 0x1c
> -#define AXI_CLKGEN_REG_LOCK2 0x20
> -#define AXI_CLKGEN_REG_LOCK3 0x24
> -#define AXI_CLKGEN_REG_FILTER1 0x28
> -#define AXI_CLKGEN_REG_FILTER2 0x2c
> +#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04
> +#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08
> +#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c
> +#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10
> +#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14
> +#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18
> +#define AXI_CLKGEN_V1_REG_LOCK1 0x1c
> +#define AXI_CLKGEN_V1_REG_LOCK2 0x20
> +#define AXI_CLKGEN_V1_REG_LOCK3 0x24
> +#define AXI_CLKGEN_V1_REG_FILTER1 0x28
> +#define AXI_CLKGEN_V1_REG_FILTER2 0x2c
> +
> +#define AXI_CLKGEN_V2_REG_RESET 0x40
> +#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70
> +#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74
> +
> +#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1)
> +#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0)
> +
> +#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29)
> +#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28)
> +
> +#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16)
> +
> +#define MMCM_REG_CLKOUT0_1 0x08
> +#define MMCM_REG_CLKOUT0_2 0x09
> +#define MMCM_REG_CLK_FB1 0x14
> +#define MMCM_REG_CLK_FB2 0x15
> +#define MMCM_REG_CLK_DIV 0x16
> +#define MMCM_REG_LOCK1 0x18
> +#define MMCM_REG_LOCK2 0x19
> +#define MMCM_REG_LOCK3 0x1a
> +#define MMCM_REG_FILTER1 0x4e
> +#define MMCM_REG_FILTER2 0x4f
> +
> +struct axi_clkgen;
> +
> +struct axi_clkgen_mmcm_ops {
> + void (*enable)(struct axi_clkgen *axi_clkgen, bool enable);
> + int (*write)(struct axi_clkgen *axi_clkgen, unsigned int reg,
> + unsigned int val, unsigned int mask);
> + int (*read)(struct axi_clkgen *axi_clkgen, unsigned int reg,
> + unsigned int *val);
> +};
>
> struct axi_clkgen {
> void __iomem *base;
> + const struct axi_clkgen_mmcm_ops *mmcm_ops;
> struct clk_hw clk_hw;
> };
>
> +static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
> + bool enable)
> +{
> + axi_clkgen->mmcm_ops->enable(axi_clkgen, enable);
> +}
> +
> +static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
> + unsigned int reg, unsigned int val, unsigned int mask)
> +{
> + return axi_clkgen->mmcm_ops->write(axi_clkgen, reg, val, mask);
> +}
> +
> +static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
> + unsigned int reg, unsigned int *val)
> +{
> + return axi_clkgen->mmcm_ops->read(axi_clkgen, reg, val);
> +}
> +
> static uint32_t axi_clkgen_lookup_filter(unsigned int m)
> {
> switch (m) {
> @@ -156,6 +208,148 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
> *val = readl(axi_clkgen->base + reg);
> }
>
> +static unsigned int axi_clkgen_v1_map_mmcm_reg(unsigned int reg)
> +{
> + switch (reg) {
> + case MMCM_REG_CLKOUT0_1:
> + return AXI_CLKGEN_V1_REG_CLK_OUT1;
> + case MMCM_REG_CLKOUT0_2:
> + return AXI_CLKGEN_V1_REG_CLK_OUT2;
> + case MMCM_REG_CLK_FB1:
> + return AXI_CLKGEN_V1_REG_CLK_FB1;
> + case MMCM_REG_CLK_FB2:
> + return AXI_CLKGEN_V1_REG_CLK_FB2;
> + case MMCM_REG_CLK_DIV:
> + return AXI_CLKGEN_V1_REG_CLK_DIV;
> + case MMCM_REG_LOCK1:
> + return AXI_CLKGEN_V1_REG_LOCK1;
> + case MMCM_REG_LOCK2:
> + return AXI_CLKGEN_V1_REG_LOCK2;
> + case MMCM_REG_LOCK3:
> + return AXI_CLKGEN_V1_REG_LOCK3;
> + case MMCM_REG_FILTER1:
> + return AXI_CLKGEN_V1_REG_FILTER1;
> + case MMCM_REG_FILTER2:
> + return AXI_CLKGEN_V1_REG_FILTER2;
> + default:
> + return 0;
> + }
> +}
> +
> +static int axi_clkgen_v1_mmcm_write(struct axi_clkgen *axi_clkgen,
> + unsigned int reg, unsigned int val, unsigned int mask)
> +{
> + reg = axi_clkgen_v1_map_mmcm_reg(reg);
> + if (reg == 0)
> + return -EINVAL;
> +
> + axi_clkgen_write(axi_clkgen, reg, val);
> +
> + return 0;
> +}
> +
> +static int axi_clkgen_v1_mmcm_read(struct axi_clkgen *axi_clkgen,
> + unsigned int reg, unsigned int *val)
> +{
> + reg = axi_clkgen_v1_map_mmcm_reg(reg);
> + if (reg == 0)
> + return -EINVAL;
> +
> + axi_clkgen_read(axi_clkgen, reg, val);
> +
> + return 0;
> +}
> +
> +static void axi_clkgen_v1_mmcm_enable(struct axi_clkgen *axi_clkgen,
> + bool enable)
> +{
> + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V1_REG_UPDATE_ENABLE, enable);
> +}
> +
> +static const struct axi_clkgen_mmcm_ops axi_clkgen_v1_mmcm_ops = {
> + .write = axi_clkgen_v1_mmcm_write,
> + .read = axi_clkgen_v1_mmcm_read,
> + .enable = axi_clkgen_v1_mmcm_enable,
> +};
> +
> +static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
> +{
> + unsigned int timeout = 10000;
> + unsigned int val;
> +
> + do {
> + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val);
> + } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout);
> +
> + if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY)
> + return -EIO;
> +
> + return val & 0xffff;
> +}
> +
> +static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen,
> + unsigned int reg, unsigned int *val)
> +{
> + unsigned int reg_val;
> + int ret;
> +
> + ret = axi_clkgen_wait_non_busy(axi_clkgen);
> + if (ret < 0)
> + return ret;
> +
> + reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ;
> + reg_val |= (reg << 16);
> +
> + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
> +
> + ret = axi_clkgen_wait_non_busy(axi_clkgen);
> + if (ret < 0)
> + return ret;
> +
> + *val = ret;
> +
> + return 0;
> +}
> +
> +static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen,
> + unsigned int reg, unsigned int val, unsigned int mask)
> +{
> + unsigned int reg_val = 0;
> + int ret;
> +
> + ret = axi_clkgen_wait_non_busy(axi_clkgen);
> + if (ret < 0)
> + return ret;
> +
> + if (mask != 0xffff) {
> + axi_clkgen_v2_mmcm_read(axi_clkgen, reg, ®_val);
> + reg_val &= ~mask;
> + }
> +
> + reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask);
> +
> + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
> +
> + return 0;
> +}
> +
> +static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen,
> + bool enable)
> +{
> + unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
> +
> + if (enable)
> + val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE;
> +
> + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val);
> +}
> +
> +static const struct axi_clkgen_mmcm_ops axi_clkgen_v2_mmcm_ops = {
> + .write = axi_clkgen_v2_mmcm_write,
> + .read = axi_clkgen_v2_mmcm_read,
> + .enable = axi_clkgen_v2_mmcm_enable,
> +};
> +
> static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
> {
> return container_of(clk_hw, struct axi_clkgen, clk_hw);
> @@ -184,33 +378,29 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
> filter = axi_clkgen_lookup_filter(m - 1);
> lock = axi_clkgen_lookup_lock(m - 1);
>
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0);
> -
> axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1,
> - (high << 6) | low);
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2,
> - (edge << 7) | (nocount << 6));
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
> + (high << 6) | low, 0xefff);
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
> + (edge << 7) | (nocount << 6), 0x03ff);
>
> axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV,
> - (edge << 13) | (nocount << 12) | (high << 6) | low);
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
> + (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
>
> axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1,
> - (high << 6) | low);
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2,
> - (edge << 7) | (nocount << 6));
> -
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff);
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2,
> - (((lock >> 16) & 0x1f) << 10) | 0x1);
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3,
> - (((lock >> 24) & 0x1f) << 10) | 0x3e9);
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16);
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter);
> -
> - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1);
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
> + (high << 6) | low, 0xefff);
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
> + (edge << 7) | (nocount << 6), 0x03ff);
> +
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
> + (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
> + (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
> + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
>
> return 0;
> }
> @@ -236,11 +426,11 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
> unsigned int reg;
> unsigned long long tmp;
>
> - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®);
> + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®);
> dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
> - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®);
> + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®);
> d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
> - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®);
> + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®);
> m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
>
> if (d == 0 || dout == 0)
> @@ -255,14 +445,45 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
> return tmp;
> }
>
> +static int axi_clkgen_enable(struct clk_hw *clk_hw)
> +{
> + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
> +
> + axi_clkgen_mmcm_enable(axi_clkgen, true);
> +
> + return 0;
> +}
> +
> +static void axi_clkgen_disable(struct clk_hw *clk_hw)
> +{
> + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
> +
> + axi_clkgen_mmcm_enable(axi_clkgen, false);
> +}
> +
> static const struct clk_ops axi_clkgen_ops = {
> .recalc_rate = axi_clkgen_recalc_rate,
> .round_rate = axi_clkgen_round_rate,
> .set_rate = axi_clkgen_set_rate,
> + .enable = axi_clkgen_enable,
> + .disable = axi_clkgen_disable,
> };
>
> +static const struct of_device_id axi_clkgen_ids[] = {
> + {
> + .compatible = "adi,axi-clkgen-1.00.a",
> + .data = &axi_clkgen_v1_mmcm_ops
> + }, {
> + .compatible = "adi,axi-clkgen-2.00.a",
> + .data = &axi_clkgen_v2_mmcm_ops,
> + },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
> +
> static int axi_clkgen_probe(struct platform_device *pdev)
> {
> + const struct of_device_id *id;
> struct axi_clkgen *axi_clkgen;
> struct clk_init_data init;
> const char *parent_name;
> @@ -270,10 +491,19 @@ static int axi_clkgen_probe(struct platform_device *pdev)
> struct resource *mem;
> struct clk *clk;
>
> + if (!pdev->dev.of_node)
> + return -ENODEV;
> +
> + id = of_match_node(axi_clkgen_ids, pdev->dev.of_node);
> + if (!id)
> + return -ENODEV;
> +
> axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
> if (!axi_clkgen)
> return -ENOMEM;
>
> + axi_clkgen->mmcm_ops = id->data;
> +
> mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
> if (IS_ERR(axi_clkgen->base))
> @@ -289,10 +519,12 @@ static int axi_clkgen_probe(struct platform_device *pdev)
>
> init.name = clk_name;
> init.ops = &axi_clkgen_ops;
> - init.flags = 0;
> + init.flags = CLK_SET_RATE_GATE;
> init.parent_names = &parent_name;
> init.num_parents = 1;
>
> + axi_clkgen_mmcm_enable(axi_clkgen, false);
> +
> axi_clkgen->clk_hw.init = &init;
> clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
> if (IS_ERR(clk))
> @@ -309,12 +541,6 @@ static int axi_clkgen_remove(struct platform_device *pdev)
> return 0;
> }
>
> -static const struct of_device_id axi_clkgen_ids[] = {
> - { .compatible = "adi,axi-clkgen-1.00.a" },
> - { },
> -};
> -MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
> -
> static struct platform_driver axi_clkgen_driver = {
> .driver = {
> .name = "adi-axi-clkgen",
> --
> 1.8.0
>
next prev parent reply other threads:[~2014-02-27 1:04 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-02-17 9:31 [PATCH resend] clk: axi-clkgen: Add support for v2 Lars-Peter Clausen
2014-02-17 9:31 ` Lars-Peter Clausen
2014-02-27 1:04 ` Mike Turquette [this message]
2014-02-27 6:20 ` Lars-Peter Clausen
2014-02-27 6:20 ` Lars-Peter Clausen
2014-03-27 2:38 ` Mike Turquette
2014-03-27 8:29 ` Lars-Peter Clausen
2014-03-27 8:29 ` Lars-Peter Clausen
-- strict thread matches above, loose matches on Subject: below --
2014-01-13 17:30 Lars-Peter Clausen
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=20140227010421.12081.82707@quantum \
--to=mturquette@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
/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.