Thread (7 messages) 7 messages, 2 authors, 2021-12-08

Re: [PATCH v6 2/2] clk: microchip: Add driver for Microchip PolarFire SoC

From: Geert Uytterhoeven <geert@linux-m68k.org>
Date: 2021-12-06 15:55:51
Also in: linux-clk

Hi Conor, Daire,

On Tue, Nov 30, 2021 at 3:06 PM [off-list ref] 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!
quoted hunk ↗ jump to hunk
--- /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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help