From: Geert Uytterhoeven <geert@linux-m68k.org>
To: Conor Dooley <conor.dooley@microchip.com>, daire.mcnamara@microchip.com
Cc: Michael Turquette <mturquette@baylibre.com>,
Stephen Boyd <sboyd@kernel.org>,
linux-clk <linux-clk@vger.kernel.org>,
Rob Herring <robh+dt@kernel.org>,
"open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS"
<devicetree@vger.kernel.org>,
Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>,
david.abdurachmanov@gmail.com,
Palmer Dabbelt <palmer@dabbelt.com>,
cyril.jean@microchip.com
Subject: Re: [PATCH v6 2/2] clk: microchip: Add driver for Microchip PolarFire SoC
Date: Mon, 6 Dec 2021 16:53:32 +0100 [thread overview]
Message-ID: <CAMuHMdV=QNtz0rRLQv+=k+GGuSyKANFjGQ1YTKaKwcmzsvf5RA@mail.gmail.com> (raw)
In-Reply-To: <20211130140724.10750-3-conor.dooley@microchip.com>
Hi Conor, Daire,
On Tue, Nov 30, 2021 at 3:06 PM <conor.dooley@microchip.com> wrote:
> From: Daire McNamara <daire.mcnamara@microchip.com>
>
> Add support for clock configuration on Microchip PolarFire SoC
>
> Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
> Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Thanks for your patch!
> --- /dev/null
> +++ b/drivers/clk/microchip/clk-mpfs.c
> @@ -0,0 +1,444 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Daire McNamara,<daire.mcnamara@microchip.com>
> + * Copyright (C) 2020 Microchip Technology Inc. All rights reserved.
> + */
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <dt-bindings/clock/microchip,mpfs-clock.h>
> +
> +/* address offset of control registers */
> +#define REG_CLOCK_CONFIG_CR 0x08u
> +#define REG_SUBBLK_CLOCK_CR 0x84u
> +#define REG_SUBBLK_RESET_CR 0x88u
> +
> +struct mpfs_clock_data {
> + void __iomem *base;
> + struct clk_hw_onecell_data hw_data;
> +};
> +
> +struct mpfs_cfg_clock {
> + unsigned int id;
implicit gap of 4 bytes
> + const char *name;
> + u8 shift;
> + u8 width;
implicit gap of 6 bytes
> + const struct clk_div_table *table;
> + unsigned long flags;
Hence you better move id, shift, and width to the end.
> +};
> +
> +struct mpfs_cfg_hw_clock {
> + struct mpfs_cfg_clock cfg;
> + void __iomem *sys_base;
> + /* lock is used to prevent multiple writes */
> + spinlock_t *lock;
> + struct clk_hw hw;
> + struct clk_init_data init;
> +};
> +
> +#define to_mpfs_cfg_clk(_hw) container_of(_hw, struct mpfs_cfg_hw_clock, hw)
> +
> +struct mpfs_periph_clock {
> + unsigned int id;
implicit gap of 4 bytes
> + const char *name;
> + u8 shift;
implicit gap of 7 bytes
> + unsigned long flags;
> +};
> +
> +struct mpfs_periph_hw_clock {
> + struct mpfs_periph_clock periph;
> + void __iomem *sys_base;
> + /* lock is used to prevent multiple writes */
> + spinlock_t *lock;
This is not used?
> + struct clk_hw hw;
> +};
> +
> +#define to_mpfs_periph_clk(_hw) container_of(_hw, struct mpfs_periph_hw_clock, hw)
> +
> +/*
> + * mpfs_clk_lock prevents anything else from writing to the
> + * mpfs clk block while a software locked register is being written.
> + */
> +static DEFINE_SPINLOCK(mpfs_clk_lock);
> +
> +static struct clk_parent_data mpfs_cfg_parent[] = {
> + { .fw_name = "msspllclk", .name = "msspllclk" },
So you rely on the name of the fixed-clock reference clock? That is
very fragile. Please instead use devm_clk_get() to get the reference
clock from the "clocks" property of the clock-controller node.
> +static unsigned long mpfs_cfg_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
> +{
> + struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
> + struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
> + void __iomem *base_addr = cfg_hw->sys_base;
> + unsigned long rate;
> + u32 val;
> +
> + val = readl_relaxed(base_addr + REG_CLOCK_CONFIG_CR) >> cfg->shift;
> + val &= clk_div_mask(cfg->width);
> + rate = prate / (1u << val);
> +
> + return rate;
return prate / (1u << val);
> +}
> +static int mpfs_cfg_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate)
> +{
> + struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
> + struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
> + void __iomem *base_addr = cfg_hw->sys_base;
> + unsigned long flags = 0;
> + u32 val;
> + int divider_setting;
> +
> + divider_setting = divider_get_val(rate, prate, cfg->table, cfg->width, cfg_hw->cfg.flags);
> +
> + if (divider_setting < 0)
> + return divider_setting;
> +
> + if (cfg_hw->lock)
Isn't this branch always taken?
> + spin_lock_irqsave(cfg_hw->lock, flags);
> + else
> + __acquire(cfg_hw->lock);
> +
> + val = readl_relaxed(base_addr + REG_CLOCK_CONFIG_CR);
> + val &= ~(clk_div_mask(cfg->width) << cfg_hw->cfg.shift);
> + val |= divider_setting << cfg->shift;
> + writel_relaxed(val, base_addr + REG_CLOCK_CONFIG_CR);
> +
> + if (cfg_hw->lock)
> + spin_unlock_irqrestore(cfg_hw->lock, flags);
> + else
> + __release(cfg_hw->lock);
> +
> + return 0;
> +}
> +static void mpfs_clk_unregister_cfg(struct device *dev, struct clk_hw *hw)
> +{
> + struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
> +
> + devm_clk_hw_unregister(dev, hw);
> + kfree(cfg_hw);
This is freeing a part of the big array allocated with devm_kzalloc()?
> +}
> +
> +static struct clk_hw *mpfs_clk_register_cfg(struct device *dev,
> + struct mpfs_cfg_hw_clock *cfg_hw,
> + void __iomem *sys_base)
> +{
> + struct clk_hw *hw;
> + int err;
> +
> + cfg_hw->sys_base = sys_base;
> + cfg_hw->lock = &mpfs_clk_lock;
> +
> + hw = &cfg_hw->hw;
> + err = devm_clk_hw_register(dev, hw);
> + if (err)
> + return ERR_PTR(err);
> +
> + return hw;
> +}
> +
> +static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hws,
> + int num_clks, struct mpfs_clock_data *data)
unsigned int num_clks
> +{
> + struct clk_hw *hw;
> + void __iomem *sys_base = data->base;
> + unsigned int i, id;
> +
> + for (i = 0; i < num_clks; i++) {
> + struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i];
> +
> + hw = mpfs_clk_register_cfg(dev, cfg_hw, sys_base);
> + if (IS_ERR(hw)) {
> + dev_err(dev, "%s: failed to register clock %s\n", __func__,
I guess the __func__ can be dropped, as the clock name is unique?
> + cfg_hw->cfg.name);
> + goto err_clk;
> + }
> +
> + id = cfg_hws[i].cfg.id;
> + data->hw_data.hws[id] = hw;
> + }
> +
> + return 0;
> +
> +err_clk:
> + while (i--)
> + mpfs_clk_unregister_cfg(dev, data->hw_data.hws[cfg_hws[i].cfg.id]);
> +
> + return PTR_ERR(hw);
> +}
> +static void mpfs_clk_unregister_periph(struct device *dev, struct clk_hw *hw)
> +{
> + struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
> +
> + devm_clk_hw_unregister(dev, hw);
> + kfree(periph_hw);'
This is freeing a part of the big array allocated with devm_kzalloc()?
> +}
> +static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_clock *periph_hws,
> + int num_clks, struct mpfs_clock_data *data)
> +{
> + struct clk_hw *hw;
> + void __iomem *sys_base = data->base;
> + unsigned int i, id;
> +
> + for (i = 0; i < num_clks; i++) {
> + struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i];
> +
> + hw = mpfs_clk_register_periph(dev, periph_hw, sys_base);
> + if (IS_ERR(hw)) {
> + dev_err(dev, "%s: failed to register clock %s\n", __func__,
I guess the __func__ can be dropped, as the clock name is unique?
> + periph_hw->periph.name);
> + goto err_clk;
> + }
> +
> + id = periph_hws[i].periph.id;
> + data->hw_data.hws[id] = hw;
> + }
> +
> + return 0;
> +
> +err_clk:
> + while (i--)
> + mpfs_clk_unregister_periph(dev, data->hw_data.hws[periph_hws[i].periph.id]);
> +
> + return PTR_ERR(hw);
> +}
> +
> +static int mpfs_clk_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct mpfs_clock_data *clk_data;
> + struct resource *res;
> + int num_clks;
unsigned int
> + int ret;
> +
> + num_clks = ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks);
As mpfs_periph_clks[] lacks an entry for CLK_RESERVED, num_clks is
one too small...
> +
> + clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL);
> + if (!clk_data)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + clk_data->base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(clk_data->base))
> + return PTR_ERR(clk_data->base);
> +
> + clk_data->hw_data.num = num_clks;
> +
> + ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data);
> + if (ret)
> + goto err_clk;
> +
> + ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks),
> + clk_data);
... and initializing CLK_CFM will write beyond the end of clk_data[].
> + if (ret)
> + goto err_clk;
> +
> + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data);
> + if (ret)
> + goto err_clk;
> +
> + dev_info(dev, "registered MPFS core clocks\n");
> + return ret;
> +
> +err_clk:
> + dev_err(dev, "failed to register MPFS core clocks\n");
> + return ret;
> +}
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
next prev parent reply other threads:[~2021-12-06 15:55 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-30 14:07 [PATCH v6 0/2] CLK: microchip: Add clkcfg driver for Microchip PolarFire SoC conor.dooley
2021-11-30 14:07 ` [PATCH v6 1/2] dt-bindings: clk: microchip: Add Microchip PolarFire host binding conor.dooley
2021-12-06 15:12 ` Geert Uytterhoeven
2021-11-30 14:07 ` [PATCH v6 2/2] clk: microchip: Add driver for Microchip PolarFire SoC conor.dooley
2021-12-06 15:53 ` Geert Uytterhoeven [this message]
2021-12-08 15:29 ` Conor.Dooley
2021-12-08 18:28 ` Geert Uytterhoeven
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='CAMuHMdV=QNtz0rRLQv+=k+GGuSyKANFjGQ1YTKaKwcmzsvf5RA@mail.gmail.com' \
--to=geert@linux-m68k.org \
--cc=conor.dooley@microchip.com \
--cc=cyril.jean@microchip.com \
--cc=daire.mcnamara@microchip.com \
--cc=david.abdurachmanov@gmail.com \
--cc=devicetree@vger.kernel.org \
--cc=krzysztof.kozlowski@canonical.com \
--cc=linux-clk@vger.kernel.org \
--cc=mturquette@baylibre.com \
--cc=palmer@dabbelt.com \
--cc=robh+dt@kernel.org \
--cc=sboyd@kernel.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 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).