[v15,2/2] clk: clk-loongson2: add clock controller driver support

Message ID 20230321130710.20236-2-zhuyinbo@loongson.cn
State New
Headers
Series [v15,1/2] dt-bindings: clock: add loongson-2 boot clock index |

Commit Message

Yinbo Zhu March 21, 2023, 1:07 p.m. UTC
  This driver provides support for clock controller on Loongson-2 SoC,
the Loongson-2 SoC uses a 100MHz clock as the PLL reference clock,
there are five independent PLLs inside, each of which PLL can
provide up to three sets of frequency dependent clock outputs.

Signed-off-by: Yinbo Zhu <zhuyinbo@loongson.cn>
---
Change in v15:
		1. Remove the depends on COMMON_CLK  && OF and add depend on
		   LOONGARCH || COMPILE_TEST.
		2. Remove some head file that include "linux/of.h", "linux/of_address.h"
		   and "linux/clk.h".
		3. Remove the initialization of .name in struct clk_parent_data.
		4. Replace device node with struct device.
		5. Replace of_clk_hw_register with devm_clk_hw_register.
		6. Remove the initialized operaion in loongson2_calc_pll_rate.
		7. Inline the function about loongson2_clocks_init.
		8. Replace of_iomap with devm_platform_ioremap_resource.
		9. Drop error messages when mapping.
		10. Replace kzalloc with devm_kzalloc.
Change in v14:
		1. For some board's config file doesn't enable CONFIG_64BIT and
		   will report readq undefine error, and this version was to
		   add include <linux/io-64-nonatomic-lo-hi.h> to fix this issue.
Change in v13:
		1. Add the Loongson-2 boot clk support.
Change in v12:
		1. Change driver register way that use platform driver register.
Change in v11:
		1. Remove the repetitive COMMON_CLK config in loongarch Kconfig.
		2. Fixup the help paragraph descprition in COMMON_CLK_LOONGSON2.
		3. Drop the <linux/clkdev.h>.
		4. Drop the cast in clock driver.
		5. Use div_u64() replace do_div().
		6. Inline this function for loongson2_check_clk_hws.
		7. Fixup the comma deposition on the loongson2_check_clk_hws.
		8. Drop the loongson2_obtain_fixed_clk_hw and rework this driver
		   use clk_parent_data and the 'fw_name'.
Change in v10:
		1. Detach of_clk_init to another patch. 
Change in v9:
		1. Add all history changelog information.
Change in v8:
		1. Remove the flag "CLK_IS_BASIC".
Change in v7:
		1. Adjust position alphabetically in Kconfig and Makefile.
		2. Add static for loongson2_pll_base.
		3. Move other file-scope variables in probe.
Change in v6:
		1. NO change, but other patch in this series of patches has
		   changes.
Change in v5:
		1. Replace loongson2 with Loongson-2 in commit info.
		2. Replace Loongson2 with Loongson-2 in binding and
		   Kconfig file.
		3. Replace soc with SoC.
Change in v4:
		1. Fixup clock-names that replace "xxx-clk" with "xxx".
Change in v3:
		1. NO change, but other patch in this series of patches has
		   changes.
Change in v2:
		1. Update the include filename.
		2. Change string from refclk/REFCLK to ref/REF.

 MAINTAINERS                 |   1 +
 drivers/clk/Kconfig         |   9 +
 drivers/clk/Makefile        |   1 +
 drivers/clk/clk-loongson2.c | 344 ++++++++++++++++++++++++++++++++++++
 4 files changed, 355 insertions(+)
 create mode 100644 drivers/clk/clk-loongson2.c
  

Comments

Stephen Boyd March 21, 2023, 11:42 p.m. UTC | #1
Quoting Yinbo Zhu (2023-03-21 06:07:10)
> diff --git a/drivers/clk/clk-loongson2.c b/drivers/clk/clk-loongson2.c
> new file mode 100644
> index 000000000000..090810655511
> --- /dev/null
> +++ b/drivers/clk/clk-loongson2.c
> @@ -0,0 +1,344 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Author: Yinbo Zhu <zhuyinbo@loongson.cn>
> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/io-64-nonatomic-lo-hi.h>
> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
> +
> +#define LOONGSON2_PLL_MULT_SHIFT               32
> +#define LOONGSON2_PLL_MULT_WIDTH               10
> +#define LOONGSON2_PLL_DIV_SHIFT                        26
> +#define LOONGSON2_PLL_DIV_WIDTH                        6
> +#define LOONGSON2_APB_FREQSCALE_SHIFT          20
> +#define LOONGSON2_APB_FREQSCALE_WIDTH          3
> +#define LOONGSON2_USB_FREQSCALE_SHIFT          16
> +#define LOONGSON2_USB_FREQSCALE_WIDTH          3
> +#define LOONGSON2_SATA_FREQSCALE_SHIFT         12
> +#define LOONGSON2_SATA_FREQSCALE_WIDTH         3
> +#define LOONGSON2_BOOT_FREQSCALE_SHIFT         8
> +#define LOONGSON2_BOOT_FREQSCALE_WIDTH         3
> +
> +static void __iomem *loongson2_pll_base;

Why is this a global?

> +
> +static const struct clk_parent_data pdata[] = {
> +       { .fw_name = "ref_100m",},
> +};
> +
> +static struct clk_hw *loongson2_clk_register(struct device *dev,
> +                                         const char *name,
> +                                         const char *parent_name,
> +                                         const struct clk_ops *ops,
> +                                         unsigned long flags)
> +{
> +       int ret;
> +       struct clk_hw *hw;
> +       struct clk_init_data init;
> +
> +       /* allocate the divider */

Remove useless comment.

> +       hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
> +       if (!hw)
> +               return ERR_PTR(-ENOMEM);
> +
> +       init.name = name;
> +       init.ops = ops;
> +       init.flags = flags;
> +       init.num_parents = 1;
> +
> +       if (!parent_name)
> +               init.parent_data = pdata;
> +       else
> +               init.parent_names = &parent_name;
> +
> +       hw->init = &init;
> +
> +       /* register the clock */

Remove useless comment.

> +       ret = devm_clk_hw_register(dev, hw);
> +       if (ret)
> +               hw = ERR_PTR(ret);
> +
> +       return hw;
> +}
> +
[....]
> +
> +static const struct clk_ops loongson2_sata_clk_ops = {
> +       .recalc_rate = loongson2_sata_recalc_rate,
> +};
> +
> +static inline void loongson2_check_clk_hws(struct clk_hw *clks[], unsigned int count)

This needs to return an error instead of be void.

> +{
> +       unsigned int i;
> +
> +       for (i = 0; i < count; i++)
> +               if (IS_ERR(clks[i]))
> +                       pr_err("Loongson2 clk %u: register failed with %ld\n",
> +                               i, PTR_ERR(clks[i]));
> +}
> +
> +static inline void loongson2_clocks_init(struct device *dev)
> +{
> +       struct clk_hw **hws;
> +       struct clk_hw_onecell_data *clk_hw_data;
> +       spinlock_t loongson2_clk_lock;
> +
> +       clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
> +                                       GFP_KERNEL);
> +       if (WARN_ON(!clk_hw_data))
> +               return;
> +
> +       clk_hw_data->num = LOONGSON2_CLK_END;
> +       hws = clk_hw_data->hws;
> +
> +       hws[LOONGSON2_NODE_PLL] = loongson2_clk_register(dev, "node_pll",
> +                                               NULL,
> +                                               &loongson2_node_clk_ops, 0);
> +
> +       hws[LOONGSON2_DDR_PLL] = loongson2_clk_register(dev, "ddr_pll",
> +                                               NULL,
> +                                               &loongson2_ddr_clk_ops, 0);
> +
> +       hws[LOONGSON2_DC_PLL] = loongson2_clk_register(dev, "dc_pll",
> +                                               NULL,
> +                                               &loongson2_dc_clk_ops, 0);
> +
> +       hws[LOONGSON2_PIX0_PLL] = loongson2_clk_register(dev, "pix0_pll",
> +                                               NULL,
> +                                               &loongson2_pix0_clk_ops, 0);
> +
> +       hws[LOONGSON2_PIX1_PLL] = loongson2_clk_register(dev, "pix1_pll",
> +                                               NULL,
> +                                               &loongson2_pix1_clk_ops, 0);
> +
> +       hws[LOONGSON2_BOOT_CLK] = loongson2_clk_register(dev, "boot",
> +                                               NULL,
> +                                               &loongson2_boot_clk_ops, 0);
> +
> +       hws[LOONGSON2_NODE_CLK] = clk_hw_register_divider(NULL, "node",

These should be devm_ variants so they're undone on failure.

> +                                               "node_pll", 0,
> +                                               loongson2_pll_base + 0x8, 0,
> +                                               6, CLK_DIVIDER_ONE_BASED,
> +                                               &loongson2_clk_lock);
> +
> +       /*
> +        * The hda clk divisor in the upper 32bits and the clk-prodiver
> +        * layer code doesn't support 64bit io operation thus a conversion
> +        * is required that subtract shift by 32 and add 4byte to the hda
> +        * address
> +        */
> +       hws[LOONGSON2_HDA_CLK] = clk_hw_register_divider(NULL, "hda",
> +                                               "ddr_pll", 0,
> +                                               loongson2_pll_base + 0x22, 12,
> +                                               7, CLK_DIVIDER_ONE_BASED,
> +                                               &loongson2_clk_lock);
> +
> +       hws[LOONGSON2_GPU_CLK] = clk_hw_register_divider(NULL, "gpu",
> +                                               "ddr_pll", 0,
> +                                               loongson2_pll_base + 0x18, 22,
> +                                               6, CLK_DIVIDER_ONE_BASED,
> +                                               &loongson2_clk_lock);
> +
> +       hws[LOONGSON2_DDR_CLK] = clk_hw_register_divider(NULL, "ddr",
> +                                               "ddr_pll", 0,
> +                                               loongson2_pll_base + 0x18, 0,
> +                                               6, CLK_DIVIDER_ONE_BASED,
> +                                               &loongson2_clk_lock);
> +
> +       hws[LOONGSON2_GMAC_CLK] = clk_hw_register_divider(NULL, "gmac",
> +                                               "dc_pll", 0,
> +                                               loongson2_pll_base + 0x28, 22,
> +                                               6, CLK_DIVIDER_ONE_BASED,
> +                                               &loongson2_clk_lock);
> +
> +       hws[LOONGSON2_DC_CLK] = clk_hw_register_divider(NULL, "dc",
> +                                               "dc_pll", 0,
> +                                               loongson2_pll_base + 0x28, 0,
> +                                               6, CLK_DIVIDER_ONE_BASED,
> +                                               &loongson2_clk_lock);
> +
> +       hws[LOONGSON2_APB_CLK] = loongson2_clk_register(dev, "apb",
> +                                               "gmac",
> +                                               &loongson2_apb_clk_ops, 0);
> +
> +       hws[LOONGSON2_USB_CLK] = loongson2_clk_register(dev, "usb",
> +                                               "gmac",
> +                                               &loongson2_usb_clk_ops, 0);
> +
> +       hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(dev, "sata",
> +                                               "gmac",
> +                                               &loongson2_sata_clk_ops, 0);
> +
> +       hws[LOONGSON2_PIX0_CLK] = clk_hw_register_divider(NULL, "pix0",
> +                                               "pix0_pll", 0,
> +                                               loongson2_pll_base + 0x38, 0, 6,
> +                                               CLK_DIVIDER_ONE_BASED,
> +                                               &loongson2_clk_lock);
> +
> +       hws[LOONGSON2_PIX1_CLK] = clk_hw_register_divider(NULL, "pix1",
> +                                               "pix1_pll", 0,
> +                                               loongson2_pll_base + 0x48, 0, 6,
> +                                               CLK_DIVIDER_ONE_BASED,
> +                                               &loongson2_clk_lock);
> +
> +       loongson2_check_clk_hws(hws, LOONGSON2_CLK_END);
> +
> +       devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data);

Return this error code.

> +}
> +
> +static const struct of_device_id loongson2_clk_match_table[] = {
> +       { .compatible = "loongson,ls2k-clk" },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);

This table can go next to the driver instead of be above probe.

> +
> +static int loongson2_clk_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +
> +       loongson2_pll_base = devm_platform_ioremap_resource(pdev, 0);
> +       if (!loongson2_pll_base)

Should be IS_ERR(loongson2_pll_base)

> +               return PTR_ERR(loongson2_pll_base);
> +
> +       loongson2_clocks_init(dev);

Please inline this function here.
  
Yinbo Zhu March 22, 2023, 2:07 a.m. UTC | #2
在 2023/3/22 上午7:42, Stephen Boyd 写道:
> Quoting Yinbo Zhu (2023-03-21 06:07:10)
>> diff --git a/drivers/clk/clk-loongson2.c b/drivers/clk/clk-loongson2.c
>> new file mode 100644
>> index 000000000000..090810655511
>> --- /dev/null
>> +++ b/drivers/clk/clk-loongson2.c
>> @@ -0,0 +1,344 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Author: Yinbo Zhu <zhuyinbo@loongson.cn>
>> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/init.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/slab.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/io-64-nonatomic-lo-hi.h>
>> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
>> +
>> +#define LOONGSON2_PLL_MULT_SHIFT               32
>> +#define LOONGSON2_PLL_MULT_WIDTH               10
>> +#define LOONGSON2_PLL_DIV_SHIFT                        26
>> +#define LOONGSON2_PLL_DIV_WIDTH                        6
>> +#define LOONGSON2_APB_FREQSCALE_SHIFT          20
>> +#define LOONGSON2_APB_FREQSCALE_WIDTH          3
>> +#define LOONGSON2_USB_FREQSCALE_SHIFT          16
>> +#define LOONGSON2_USB_FREQSCALE_WIDTH          3
>> +#define LOONGSON2_SATA_FREQSCALE_SHIFT         12
>> +#define LOONGSON2_SATA_FREQSCALE_WIDTH         3
>> +#define LOONGSON2_BOOT_FREQSCALE_SHIFT         8
>> +#define LOONGSON2_BOOT_FREQSCALE_WIDTH         3
>> +
>> +static void __iomem *loongson2_pll_base;
> Why is this a global?

Normally, this should be an element in the structure, but since there is 
only one

element, It is directly defined as a global variable for use by other 
function.

>
>> +
>> +static const struct clk_parent_data pdata[] = {
>> +       { .fw_name = "ref_100m",},
>> +};
>> +
>> +static struct clk_hw *loongson2_clk_register(struct device *dev,
>> +                                         const char *name,
>> +                                         const char *parent_name,
>> +                                         const struct clk_ops *ops,
>> +                                         unsigned long flags)
>> +{
>> +       int ret;
>> +       struct clk_hw *hw;
>> +       struct clk_init_data init;
>> +
>> +       /* allocate the divider */
> Remove useless comment.
okay , I will remove it.
>
>> +       hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
>> +       if (!hw)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       init.name = name;
>> +       init.ops = ops;
>> +       init.flags = flags;
>> +       init.num_parents = 1;
>> +
>> +       if (!parent_name)
>> +               init.parent_data = pdata;
>> +       else
>> +               init.parent_names = &parent_name;
>> +
>> +       hw->init = &init;
>> +
>> +       /* register the clock */
> Remove useless comment.
okay, I will remove it.
>
>> +       ret = devm_clk_hw_register(dev, hw);
>> +       if (ret)
>> +               hw = ERR_PTR(ret);
>> +
>> +       return hw;
>> +}
>> +
> [....]
>> +
>> +static const struct clk_ops loongson2_sata_clk_ops = {
>> +       .recalc_rate = loongson2_sata_recalc_rate,
>> +};
>> +
>> +static inline void loongson2_check_clk_hws(struct clk_hw *clks[], unsigned int count)
> This needs to return an error instead of be void.
okay, I got it.
>
>> +{
>> +       unsigned int i;
>> +
>> +       for (i = 0; i < count; i++)
>> +               if (IS_ERR(clks[i]))
>> +                       pr_err("Loongson2 clk %u: register failed with %ld\n",
>> +                               i, PTR_ERR(clks[i]));
>> +}
>> +
>> +static inline void loongson2_clocks_init(struct device *dev)
>> +{
>> +       struct clk_hw **hws;
>> +       struct clk_hw_onecell_data *clk_hw_data;
>> +       spinlock_t loongson2_clk_lock;
>> +
>> +       clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
>> +                                       GFP_KERNEL);
>> +       if (WARN_ON(!clk_hw_data))
>> +               return;
>> +
>> +       clk_hw_data->num = LOONGSON2_CLK_END;
>> +       hws = clk_hw_data->hws;
>> +
>> +       hws[LOONGSON2_NODE_PLL] = loongson2_clk_register(dev, "node_pll",
>> +                                               NULL,
>> +                                               &loongson2_node_clk_ops, 0);
>> +
>> +       hws[LOONGSON2_DDR_PLL] = loongson2_clk_register(dev, "ddr_pll",
>> +                                               NULL,
>> +                                               &loongson2_ddr_clk_ops, 0);
>> +
>> +       hws[LOONGSON2_DC_PLL] = loongson2_clk_register(dev, "dc_pll",
>> +                                               NULL,
>> +                                               &loongson2_dc_clk_ops, 0);
>> +
>> +       hws[LOONGSON2_PIX0_PLL] = loongson2_clk_register(dev, "pix0_pll",
>> +                                               NULL,
>> +                                               &loongson2_pix0_clk_ops, 0);
>> +
>> +       hws[LOONGSON2_PIX1_PLL] = loongson2_clk_register(dev, "pix1_pll",
>> +                                               NULL,
>> +                                               &loongson2_pix1_clk_ops, 0);
>> +
>> +       hws[LOONGSON2_BOOT_CLK] = loongson2_clk_register(dev, "boot",
>> +                                               NULL,
>> +                                               &loongson2_boot_clk_ops, 0);
>> +
>> +       hws[LOONGSON2_NODE_CLK] = clk_hw_register_divider(NULL, "node",
> These should be devm_ variants so they're undone on failure.
okay , I will use devm_clk_hw_register_divider
>
>> +                                               "node_pll", 0,
>> +                                               loongson2_pll_base + 0x8, 0,
>> +                                               6, CLK_DIVIDER_ONE_BASED,
>> +                                               &loongson2_clk_lock);
>> +
>> +       /*
>> +        * The hda clk divisor in the upper 32bits and the clk-prodiver
>> +        * layer code doesn't support 64bit io operation thus a conversion
>> +        * is required that subtract shift by 32 and add 4byte to the hda
>> +        * address
>> +        */
>> +       hws[LOONGSON2_HDA_CLK] = clk_hw_register_divider(NULL, "hda",
>> +                                               "ddr_pll", 0,
>> +                                               loongson2_pll_base + 0x22, 12,
>> +                                               7, CLK_DIVIDER_ONE_BASED,
>> +                                               &loongson2_clk_lock);
>> +
>> +       hws[LOONGSON2_GPU_CLK] = clk_hw_register_divider(NULL, "gpu",
>> +                                               "ddr_pll", 0,
>> +                                               loongson2_pll_base + 0x18, 22,
>> +                                               6, CLK_DIVIDER_ONE_BASED,
>> +                                               &loongson2_clk_lock);
>> +
>> +       hws[LOONGSON2_DDR_CLK] = clk_hw_register_divider(NULL, "ddr",
>> +                                               "ddr_pll", 0,
>> +                                               loongson2_pll_base + 0x18, 0,
>> +                                               6, CLK_DIVIDER_ONE_BASED,
>> +                                               &loongson2_clk_lock);
>> +
>> +       hws[LOONGSON2_GMAC_CLK] = clk_hw_register_divider(NULL, "gmac",
>> +                                               "dc_pll", 0,
>> +                                               loongson2_pll_base + 0x28, 22,
>> +                                               6, CLK_DIVIDER_ONE_BASED,
>> +                                               &loongson2_clk_lock);
>> +
>> +       hws[LOONGSON2_DC_CLK] = clk_hw_register_divider(NULL, "dc",
>> +                                               "dc_pll", 0,
>> +                                               loongson2_pll_base + 0x28, 0,
>> +                                               6, CLK_DIVIDER_ONE_BASED,
>> +                                               &loongson2_clk_lock);
>> +
>> +       hws[LOONGSON2_APB_CLK] = loongson2_clk_register(dev, "apb",
>> +                                               "gmac",
>> +                                               &loongson2_apb_clk_ops, 0);
>> +
>> +       hws[LOONGSON2_USB_CLK] = loongson2_clk_register(dev, "usb",
>> +                                               "gmac",
>> +                                               &loongson2_usb_clk_ops, 0);
>> +
>> +       hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(dev, "sata",
>> +                                               "gmac",
>> +                                               &loongson2_sata_clk_ops, 0);
>> +
>> +       hws[LOONGSON2_PIX0_CLK] = clk_hw_register_divider(NULL, "pix0",
>> +                                               "pix0_pll", 0,
>> +                                               loongson2_pll_base + 0x38, 0, 6,
>> +                                               CLK_DIVIDER_ONE_BASED,
>> +                                               &loongson2_clk_lock);
>> +
>> +       hws[LOONGSON2_PIX1_CLK] = clk_hw_register_divider(NULL, "pix1",
>> +                                               "pix1_pll", 0,
>> +                                               loongson2_pll_base + 0x48, 0, 6,
>> +                                               CLK_DIVIDER_ONE_BASED,
>> +                                               &loongson2_clk_lock);
>> +
>> +       loongson2_check_clk_hws(hws, LOONGSON2_CLK_END);
>> +
>> +       devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data);
> Return this error code.
okay, I got it.
>
>> +}
>> +
>> +static const struct of_device_id loongson2_clk_match_table[] = {
>> +       { .compatible = "loongson,ls2k-clk" },
>> +       { }
>> +};
>> +MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
> This table can go next to the driver instead of be above probe.
okay, I will move it above loongson2_clk_driver.
>
>> +
>> +static int loongson2_clk_probe(struct platform_device *pdev)
>> +{
>> +       struct device *dev = &pdev->dev;
>> +
>> +       loongson2_pll_base = devm_platform_ioremap_resource(pdev, 0);
>> +       if (!loongson2_pll_base)
> Should be IS_ERR(loongson2_pll_base)
okay, I got it.
>
>> +               return PTR_ERR(loongson2_pll_base);
>> +
>> +       loongson2_clocks_init(dev);
> Please inline this function here.

The loongson2_clocks_init had inline in this version, or do you mean something else ? Need to inline the probe?
  
Stephen Boyd March 22, 2023, 3:16 p.m. UTC | #3
Quoting zhuyinbo (2023-03-21 19:07:26)
> 
> 在 2023/3/22 上午7:42, Stephen Boyd 写道:
> > Quoting Yinbo Zhu (2023-03-21 06:07:10)
> >> diff --git a/drivers/clk/clk-loongson2.c b/drivers/clk/clk-loongson2.c
> >> new file mode 100644
> >> index 000000000000..090810655511
> >> --- /dev/null
> >> +++ b/drivers/clk/clk-loongson2.c
> >> @@ -0,0 +1,344 @@
[....]
> >> +
> >> +static void __iomem *loongson2_pll_base;
> > Why is this a global?
> 
> Normally, this should be an element in the structure, but since there is 
> only one
> 
> element, It is directly defined as a global variable for use by other 
> function.
> 

Ok. Globals are frowned upon but it's just a single pointers so I guess
it's Ok.

> >
> >> +               return PTR_ERR(loongson2_pll_base);
> >> +
> >> +       loongson2_clocks_init(dev);
> > Please inline this function here.
> 
> The loongson2_clocks_init had inline in this version, or do you mean something else ? Need to inline the probe?

I mean get rid of the function loongson2_clocks_init() and move the code
in that function into this probe function. With this patch the probe is
split to two functions when it should be one function.
  
Yinbo Zhu March 23, 2023, 1:24 a.m. UTC | #4
在 2023/3/22 下午11:16, Stephen Boyd 写道:
> Quoting zhuyinbo (2023-03-21 19:07:26)
>> 在 2023/3/22 上午7:42, Stephen Boyd 写道:
>>> Quoting Yinbo Zhu (2023-03-21 06:07:10)
>>>> diff --git a/drivers/clk/clk-loongson2.c b/drivers/clk/clk-loongson2.c
>>>> new file mode 100644
>>>> index 000000000000..090810655511
>>>> --- /dev/null
>>>> +++ b/drivers/clk/clk-loongson2.c
>>>> @@ -0,0 +1,344 @@
> [....]
>>>> +
>>>> +static void __iomem *loongson2_pll_base;
>>> Why is this a global?
>> Normally, this should be an element in the structure, but since there is
>> only one
>>
>> element, It is directly defined as a global variable for use by other
>> function.
>>
> Ok. Globals are frowned upon but it's just a single pointers so I guess
> it's Ok.
>
>>>> +               return PTR_ERR(loongson2_pll_base);
>>>> +
>>>> +       loongson2_clocks_init(dev);
>>> Please inline this function here.
>> The loongson2_clocks_init had inline in this version, or do you mean something else ? Need to inline the probe?
> I mean get rid of the function loongson2_clocks_init() and move the code
> in that function into this probe function. With this patch the probe is
> split to two functions when it should be one function.

okay, I got it.


Thanks.
  

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 25a0981c74b6..a6983bf85956 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12155,6 +12155,7 @@  M:	Yinbo Zhu <zhuyinbo@loongson.cn>
 L:	linux-clk@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/clock/loongson,ls2k-clk.yaml
+F:	drivers/clk/clk-loongson2.c
 F:	include/dt-bindings/clock/loongson,ls2k-clk.h
 
 LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 1eef05bb1f99..6da372fcc56a 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -325,6 +325,15 @@  config COMMON_CLK_LOCHNAGAR
 	  This driver supports the clocking features of the Cirrus Logic
 	  Lochnagar audio development board.
 
+config COMMON_CLK_LOONGSON2
+	bool "Clock driver for Loongson-2 SoC"
+	depends on LOONGARCH || COMPILE_TEST
+	help
+          This driver provides support for clock controller on Loongson-2 SoC.
+          The clock controller can generates and supplies clock to various
+          peripherals within the SoC.
+          Say Y here to support Loongson-2 SoC clock driver.
+
 config COMMON_CLK_NXP
 	def_bool COMMON_CLK && (ARCH_LPC18XX || ARCH_LPC32XX)
 	select REGMAP_MMIO if ARCH_LPC32XX
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e3ca0d058a25..b298c5dabc1a 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -43,6 +43,7 @@  obj-$(CONFIG_COMMON_CLK_K210)		+= clk-k210.o
 obj-$(CONFIG_LMK04832)			+= clk-lmk04832.o
 obj-$(CONFIG_COMMON_CLK_LAN966X)	+= clk-lan966x.o
 obj-$(CONFIG_COMMON_CLK_LOCHNAGAR)	+= clk-lochnagar.o
+obj-$(CONFIG_COMMON_CLK_LOONGSON2)	+= clk-loongson2.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
 obj-$(CONFIG_COMMON_CLK_MAX9485)	+= clk-max9485.o
 obj-$(CONFIG_ARCH_MILBEAUT_M10V)	+= clk-milbeaut.o
diff --git a/drivers/clk/clk-loongson2.c b/drivers/clk/clk-loongson2.c
new file mode 100644
index 000000000000..090810655511
--- /dev/null
+++ b/drivers/clk/clk-loongson2.c
@@ -0,0 +1,344 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Author: Yinbo Zhu <zhuyinbo@loongson.cn>
+ * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <dt-bindings/clock/loongson,ls2k-clk.h>
+
+#define LOONGSON2_PLL_MULT_SHIFT		32
+#define LOONGSON2_PLL_MULT_WIDTH		10
+#define LOONGSON2_PLL_DIV_SHIFT			26
+#define LOONGSON2_PLL_DIV_WIDTH			6
+#define LOONGSON2_APB_FREQSCALE_SHIFT		20
+#define LOONGSON2_APB_FREQSCALE_WIDTH		3
+#define LOONGSON2_USB_FREQSCALE_SHIFT		16
+#define LOONGSON2_USB_FREQSCALE_WIDTH		3
+#define LOONGSON2_SATA_FREQSCALE_SHIFT		12
+#define LOONGSON2_SATA_FREQSCALE_WIDTH		3
+#define LOONGSON2_BOOT_FREQSCALE_SHIFT		8
+#define LOONGSON2_BOOT_FREQSCALE_WIDTH		3
+
+static void __iomem *loongson2_pll_base;
+
+static const struct clk_parent_data pdata[] = {
+	{ .fw_name = "ref_100m",},
+};
+
+static struct clk_hw *loongson2_clk_register(struct device *dev,
+					  const char *name,
+					  const char *parent_name,
+					  const struct clk_ops *ops,
+					  unsigned long flags)
+{
+	int ret;
+	struct clk_hw *hw;
+	struct clk_init_data init;
+
+	/* allocate the divider */
+	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
+	if (!hw)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = ops;
+	init.flags = flags;
+	init.num_parents = 1;
+
+	if (!parent_name)
+		init.parent_data = pdata;
+	else
+		init.parent_names = &parent_name;
+
+	hw->init = &init;
+
+	/* register the clock */
+	ret = devm_clk_hw_register(dev, hw);
+	if (ret)
+		hw = ERR_PTR(ret);
+
+	return hw;
+}
+
+static unsigned long loongson2_calc_pll_rate(int offset, unsigned long rate)
+{
+	u64 val;
+	u32 mult, div;
+
+	val = readq(loongson2_pll_base + offset);
+
+	mult = (val >> LOONGSON2_PLL_MULT_SHIFT) &
+			clk_div_mask(LOONGSON2_PLL_MULT_WIDTH);
+	div = (val >> LOONGSON2_PLL_DIV_SHIFT) &
+			clk_div_mask(LOONGSON2_PLL_DIV_WIDTH);
+
+	return div_u64((u64)rate * mult, div);
+}
+
+static unsigned long loongson2_node_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return loongson2_calc_pll_rate(0x0, parent_rate);
+}
+
+static const struct clk_ops loongson2_node_clk_ops = {
+	.recalc_rate = loongson2_node_recalc_rate,
+};
+
+static unsigned long loongson2_ddr_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return loongson2_calc_pll_rate(0x10, parent_rate);
+}
+
+static const struct clk_ops loongson2_ddr_clk_ops = {
+	.recalc_rate = loongson2_ddr_recalc_rate,
+};
+
+static unsigned long loongson2_dc_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return loongson2_calc_pll_rate(0x20, parent_rate);
+}
+
+static const struct clk_ops loongson2_dc_clk_ops = {
+	.recalc_rate = loongson2_dc_recalc_rate,
+};
+
+static unsigned long loongson2_pix0_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return loongson2_calc_pll_rate(0x30, parent_rate);
+}
+
+static const struct clk_ops loongson2_pix0_clk_ops = {
+	.recalc_rate = loongson2_pix0_recalc_rate,
+};
+
+static unsigned long loongson2_pix1_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return loongson2_calc_pll_rate(0x40, parent_rate);
+}
+
+static const struct clk_ops loongson2_pix1_clk_ops = {
+	.recalc_rate = loongson2_pix1_recalc_rate,
+};
+
+static unsigned long loongson2_calc_rate(unsigned long rate,
+					 int shift, int width)
+{
+	u64 val;
+	u32 mult;
+
+	val = readq(loongson2_pll_base + 0x50);
+
+	mult = (val >> shift) & clk_div_mask(width);
+
+	return div_u64((u64)rate * (mult + 1), 8);
+}
+
+static unsigned long loongson2_boot_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return loongson2_calc_rate(parent_rate,
+				   LOONGSON2_BOOT_FREQSCALE_SHIFT,
+				   LOONGSON2_BOOT_FREQSCALE_WIDTH);
+}
+
+static const struct clk_ops loongson2_boot_clk_ops = {
+	.recalc_rate = loongson2_boot_recalc_rate,
+};
+
+static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return loongson2_calc_rate(parent_rate,
+				   LOONGSON2_APB_FREQSCALE_SHIFT,
+				   LOONGSON2_APB_FREQSCALE_WIDTH);
+}
+
+static const struct clk_ops loongson2_apb_clk_ops = {
+	.recalc_rate = loongson2_apb_recalc_rate,
+};
+
+static unsigned long loongson2_usb_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return loongson2_calc_rate(parent_rate,
+				   LOONGSON2_USB_FREQSCALE_SHIFT,
+				   LOONGSON2_USB_FREQSCALE_WIDTH);
+}
+
+static const struct clk_ops loongson2_usb_clk_ops = {
+	.recalc_rate = loongson2_usb_recalc_rate,
+};
+
+static unsigned long loongson2_sata_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return loongson2_calc_rate(parent_rate,
+				   LOONGSON2_SATA_FREQSCALE_SHIFT,
+				   LOONGSON2_SATA_FREQSCALE_WIDTH);
+}
+
+static const struct clk_ops loongson2_sata_clk_ops = {
+	.recalc_rate = loongson2_sata_recalc_rate,
+};
+
+static inline void loongson2_check_clk_hws(struct clk_hw *clks[], unsigned int count)
+{
+	unsigned int i;
+
+	for (i = 0; i < count; i++)
+		if (IS_ERR(clks[i]))
+			pr_err("Loongson2 clk %u: register failed with %ld\n",
+				i, PTR_ERR(clks[i]));
+}
+
+static inline void loongson2_clocks_init(struct device *dev)
+{
+	struct clk_hw **hws;
+	struct clk_hw_onecell_data *clk_hw_data;
+	spinlock_t loongson2_clk_lock;
+
+	clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
+					GFP_KERNEL);
+	if (WARN_ON(!clk_hw_data))
+		return;
+
+	clk_hw_data->num = LOONGSON2_CLK_END;
+	hws = clk_hw_data->hws;
+
+	hws[LOONGSON2_NODE_PLL] = loongson2_clk_register(dev, "node_pll",
+						NULL,
+						&loongson2_node_clk_ops, 0);
+
+	hws[LOONGSON2_DDR_PLL] = loongson2_clk_register(dev, "ddr_pll",
+						NULL,
+						&loongson2_ddr_clk_ops, 0);
+
+	hws[LOONGSON2_DC_PLL] = loongson2_clk_register(dev, "dc_pll",
+						NULL,
+						&loongson2_dc_clk_ops, 0);
+
+	hws[LOONGSON2_PIX0_PLL] = loongson2_clk_register(dev, "pix0_pll",
+						NULL,
+						&loongson2_pix0_clk_ops, 0);
+
+	hws[LOONGSON2_PIX1_PLL] = loongson2_clk_register(dev, "pix1_pll",
+						NULL,
+						&loongson2_pix1_clk_ops, 0);
+
+	hws[LOONGSON2_BOOT_CLK] = loongson2_clk_register(dev, "boot",
+						NULL,
+						&loongson2_boot_clk_ops, 0);
+
+	hws[LOONGSON2_NODE_CLK] = clk_hw_register_divider(NULL, "node",
+						"node_pll", 0,
+						loongson2_pll_base + 0x8, 0,
+						6, CLK_DIVIDER_ONE_BASED,
+						&loongson2_clk_lock);
+
+	/*
+	 * The hda clk divisor in the upper 32bits and the clk-prodiver
+	 * layer code doesn't support 64bit io operation thus a conversion
+	 * is required that subtract shift by 32 and add 4byte to the hda
+	 * address
+	 */
+	hws[LOONGSON2_HDA_CLK] = clk_hw_register_divider(NULL, "hda",
+						"ddr_pll", 0,
+						loongson2_pll_base + 0x22, 12,
+						7, CLK_DIVIDER_ONE_BASED,
+						&loongson2_clk_lock);
+
+	hws[LOONGSON2_GPU_CLK] = clk_hw_register_divider(NULL, "gpu",
+						"ddr_pll", 0,
+						loongson2_pll_base + 0x18, 22,
+						6, CLK_DIVIDER_ONE_BASED,
+						&loongson2_clk_lock);
+
+	hws[LOONGSON2_DDR_CLK] = clk_hw_register_divider(NULL, "ddr",
+						"ddr_pll", 0,
+						loongson2_pll_base + 0x18, 0,
+						6, CLK_DIVIDER_ONE_BASED,
+						&loongson2_clk_lock);
+
+	hws[LOONGSON2_GMAC_CLK] = clk_hw_register_divider(NULL, "gmac",
+						"dc_pll", 0,
+						loongson2_pll_base + 0x28, 22,
+						6, CLK_DIVIDER_ONE_BASED,
+						&loongson2_clk_lock);
+
+	hws[LOONGSON2_DC_CLK] = clk_hw_register_divider(NULL, "dc",
+						"dc_pll", 0,
+						loongson2_pll_base + 0x28, 0,
+						6, CLK_DIVIDER_ONE_BASED,
+						&loongson2_clk_lock);
+
+	hws[LOONGSON2_APB_CLK] = loongson2_clk_register(dev, "apb",
+						"gmac",
+						&loongson2_apb_clk_ops, 0);
+
+	hws[LOONGSON2_USB_CLK] = loongson2_clk_register(dev, "usb",
+						"gmac",
+						&loongson2_usb_clk_ops, 0);
+
+	hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(dev, "sata",
+						"gmac",
+						&loongson2_sata_clk_ops, 0);
+
+	hws[LOONGSON2_PIX0_CLK] = clk_hw_register_divider(NULL, "pix0",
+						"pix0_pll", 0,
+						loongson2_pll_base + 0x38, 0, 6,
+						CLK_DIVIDER_ONE_BASED,
+						&loongson2_clk_lock);
+
+	hws[LOONGSON2_PIX1_CLK] = clk_hw_register_divider(NULL, "pix1",
+						"pix1_pll", 0,
+						loongson2_pll_base + 0x48, 0, 6,
+						CLK_DIVIDER_ONE_BASED,
+						&loongson2_clk_lock);
+
+	loongson2_check_clk_hws(hws, LOONGSON2_CLK_END);
+
+	devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data);
+}
+
+static const struct of_device_id loongson2_clk_match_table[] = {
+	{ .compatible = "loongson,ls2k-clk" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
+
+static int loongson2_clk_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	loongson2_pll_base = devm_platform_ioremap_resource(pdev, 0);
+	if (!loongson2_pll_base)
+		return PTR_ERR(loongson2_pll_base);
+
+	loongson2_clocks_init(dev);
+
+	return 0;
+}
+
+static struct platform_driver loongson2_clk_driver = {
+	.probe	= loongson2_clk_probe,
+	.driver	= {
+		.name	= "loongson2-clk",
+		.of_match_table	= loongson2_clk_match_table,
+	},
+};
+module_platform_driver(loongson2_clk_driver);
+
+MODULE_DESCRIPTION("Loongson2 clock driver");
+MODULE_LICENSE("GPL");