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 <[email protected]>
---
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 +
arch/loongarch/Kconfig | 1 +
drivers/clk/Kconfig | 9 ++
drivers/clk/Makefile | 1 +
drivers/clk/clk-loongson2.c | 286 ++++++++++++++++++++++++++++++++++++
5 files changed, 298 insertions(+)
create mode 100644 drivers/clk/clk-loongson2.c
diff --git a/MAINTAINERS b/MAINTAINERS
index ab94893fe2f6..73fa56f1fd5d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12025,6 +12025,7 @@ LOONGSON-2 SOC SERIES CLOCK DRIVER
M: Yinbo Zhu <[email protected]>
L: [email protected]
S: Maintained
+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/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 903096bd87f8..4f8f1b8f796d 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -127,6 +127,7 @@ config LOONGARCH
select USE_PERCPU_NUMA_NODE_ID
select USER_STACKTRACE_SUPPORT
select ZONE_DMA32
+ select COMMON_CLK
config 32BIT
bool
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index d79905f3e174..d13626f63739 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -326,6 +326,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 COMMON_CLK && OF
+ help
+ This driver provides support for Clock Controller that base on
+ Common Clock Framework Controller (CCF) on Loongson-2 SoC. The
+ Clock Controller can generates and supplies clock to various
+ peripherals within the SoC.
+
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..7487effceeab
--- /dev/null
+++ b/drivers/clk/clk-loongson2.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Author: Yinbo Zhu <[email protected]>
+ * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <dt-bindings/clock/loongson,ls2k-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/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
+
+static void __iomem *loongson2_pll_base;
+
+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 = kzalloc(sizeof(*hw), GFP_KERNEL);
+ if (!hw)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = ops;
+ init.flags = flags;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ hw->init = &init;
+
+ /* register the clock */
+ ret = clk_hw_register(dev, hw);
+ if (ret) {
+ kfree(hw);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
+
+static struct clk_hw *loongson2_clk_pll_register(const char *name,
+ const char *parent, void __iomem *reg)
+{
+ u64 val;
+ u32 mult = 1, div = 1;
+
+ val = readq((void *)reg);
+
+ 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 clk_hw_register_fixed_factor(NULL, name, parent,
+ CLK_SET_RATE_PARENT, mult, div);
+}
+
+static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u64 val;
+ u32 mult;
+ unsigned long rate;
+
+ val = readq((void *)(loongson2_pll_base + 0x50));
+
+ mult = (val >> LOONGSON2_APB_FREQSCALE_SHIFT) &
+ clk_div_mask(LOONGSON2_APB_FREQSCALE_WIDTH);
+
+ rate = parent_rate * (mult + 1);
+ do_div(rate, 8);
+
+ return rate;
+}
+
+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)
+{
+ u64 val;
+ u32 mult;
+ unsigned long rate;
+
+ val = readq((void *)(loongson2_pll_base + 0x50));
+
+ mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
+ clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
+
+ rate = parent_rate * (mult + 1);
+ do_div(rate, 8);
+
+ return rate;
+}
+
+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)
+{
+ u64 val;
+ u32 mult;
+ unsigned long rate;
+
+ val = readq((void *)(loongson2_pll_base + 0x50));
+
+ mult = (val >> LOONGSON2_SATA_FREQSCALE_SHIFT) &
+ clk_div_mask(LOONGSON2_SATA_FREQSCALE_WIDTH);
+
+ rate = parent_rate * (mult + 1);
+ do_div(rate, 8);
+
+ return rate;
+}
+
+static const struct clk_ops loongson2_sata_clk_ops = {
+ .recalc_rate = loongson2_sata_recalc_rate,
+};
+
+static 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 struct clk_hw *loongson2_obtain_fixed_clk_hw(
+ struct device_node *np,
+ const char *name)
+{
+ struct clk *clk;
+
+ clk = of_clk_get_by_name(np, name);
+ if (IS_ERR(clk))
+ return ERR_PTR(-ENOENT);
+
+ return __clk_get_hw(clk);
+}
+
+static void __init loongson2_clocks_init(struct device_node *np)
+{
+ struct clk_hw **hws;
+ struct clk_hw_onecell_data *clk_hw_data;
+ spinlock_t loongson2_clk_lock;
+
+ loongson2_pll_base = of_iomap(np, 0);
+
+ if (!loongson2_pll_base) {
+ pr_err("clk: unable to map loongson2 clk registers\n");
+ goto err;
+ }
+
+ clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
+ GFP_KERNEL);
+ if (WARN_ON(!clk_hw_data))
+ goto err;
+
+ clk_hw_data->num = LOONGSON2_CLK_END;
+ hws = clk_hw_data->hws;
+
+ hws[LOONGSON2_REF_100M] = loongson2_obtain_fixed_clk_hw(np,
+ "ref_100m");
+
+ hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
+ "ref_100m",
+ loongson2_pll_base);
+
+ hws[LOONGSON2_DDR_PLL] = loongson2_clk_pll_register("ddr_pll",
+ "ref_100m",
+ loongson2_pll_base + 0x10);
+
+ hws[LOONGSON2_DC_PLL] = loongson2_clk_pll_register("dc_pll",
+ "ref_100m",
+ loongson2_pll_base + 0x20);
+
+ hws[LOONGSON2_PIX0_PLL] = loongson2_clk_pll_register("pix0_pll",
+ "ref_100m",
+ loongson2_pll_base + 0x30);
+
+ hws[LOONGSON2_PIX1_PLL] = loongson2_clk_pll_register("pix1_pll",
+ "ref_100m",
+ loongson2_pll_base + 0x40);
+
+ 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(NULL, "apb",
+ "gmac",
+ &loongson2_apb_clk_ops, 0);
+
+ hws[LOONGSON2_USB_CLK] = loongson2_clk_register(NULL, "usb",
+ "gmac",
+ &loongson2_usb_clk_ops, 0);
+
+ hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(NULL, "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);
+
+ of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
+
+err:
+ iounmap(loongson2_pll_base);
+}
+
+CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
--
2.31.1
hi, yinbo
On 2022/11/29 11:41, Yinbo Zhu wrote:
> 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 <[email protected]>
> ---
> 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 +
> arch/loongarch/Kconfig | 1 +
> drivers/clk/Kconfig | 9 ++
> drivers/clk/Makefile | 1 +
> drivers/clk/clk-loongson2.c | 286 ++++++++++++++++++++++++++++++++++++
> 5 files changed, 298 insertions(+)
> create mode 100644 drivers/clk/clk-loongson2.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ab94893fe2f6..73fa56f1fd5d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12025,6 +12025,7 @@ LOONGSON-2 SOC SERIES CLOCK DRIVER
> M: Yinbo Zhu <[email protected]>
> L: [email protected]
> S: Maintained
> +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/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 903096bd87f8..4f8f1b8f796d 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -127,6 +127,7 @@ config LOONGARCH
> select USE_PERCPU_NUMA_NODE_ID
> select USER_STACKTRACE_SUPPORT
> select ZONE_DMA32
> + select COMMON_CLK
>
> config 32BIT
> bool
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index d79905f3e174..d13626f63739 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -326,6 +326,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 COMMON_CLK && OF
> + help
> + This driver provides support for Clock Controller that base on
> + Common Clock Framework Controller (CCF) on Loongson-2 SoC. The
> + Clock Controller can generates and supplies clock to various
> + peripherals within the SoC.
> +
> 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..7487effceeab
> --- /dev/null
> +++ b/drivers/clk/clk-loongson2.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Author: Yinbo Zhu <[email protected]>
> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/clkdev.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/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
> +
> +static void __iomem *loongson2_pll_base;
> +
> +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 = kzalloc(sizeof(*hw), GFP_KERNEL);
> + if (!hw)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = ops;
> + init.flags = flags;
> + init.parent_names = (parent_name ? &parent_name : NULL);
> + init.num_parents = (parent_name ? 1 : 0);
> + hw->init = &init;
> +
> + /* register the clock */
> + ret = clk_hw_register(dev, hw);
> + if (ret) {
> + kfree(hw);
> + hw = ERR_PTR(ret);
> + }
> +
> + return hw;
> +}
> +
> +static struct clk_hw *loongson2_clk_pll_register(const char *name,
> + const char *parent, void __iomem *reg)
> +{
> + u64 val;
> + u32 mult = 1, div = 1;
> +
> + val = readq((void *)reg);
> +
> + 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 clk_hw_register_fixed_factor(NULL, name, parent,
> + CLK_SET_RATE_PARENT, mult, div);
> +}
> +
> +static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_APB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_APB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
it may be more common to use macro defintions instead of magic number.
the clock configuration logic of 2k500 is same as 2k1000, only the
base and offset are different。
> +
> + mult = (val >> LOONGSON2_SATA_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_SATA_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +static const struct clk_ops loongson2_sata_clk_ops = {
> + .recalc_rate = loongson2_sata_recalc_rate,
> +};
> +
> +static 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 struct clk_hw *loongson2_obtain_fixed_clk_hw(
> + struct device_node *np,
> + const char *name)
> +{
> + struct clk *clk;
> +
> + clk = of_clk_get_by_name(np, name);
> + if (IS_ERR(clk))
> + return ERR_PTR(-ENOENT);
> +
> + return __clk_get_hw(clk);
> +}
> +
> +static void __init loongson2_clocks_init(struct device_node *np)
> +{
> + struct clk_hw **hws;
> + struct clk_hw_onecell_data *clk_hw_data;
> + spinlock_t loongson2_clk_lock;
> +
> + loongson2_pll_base = of_iomap(np, 0);
> +
> + if (!loongson2_pll_base) {
> + pr_err("clk: unable to map loongson2 clk registers\n");
> + goto err;
> + }
> +
> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
> + GFP_KERNEL);
> + if (WARN_ON(!clk_hw_data))
> + goto err;
> +
> + clk_hw_data->num = LOONGSON2_CLK_END;
> + hws = clk_hw_data->hws;
> +
> + hws[LOONGSON2_REF_100M] = loongson2_obtain_fixed_clk_hw(np,
> + "ref_100m");
> +
> + hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
> + "ref_100m",
> + loongson2_pll_base);
> +
> + hws[LOONGSON2_DDR_PLL] = loongson2_clk_pll_register("ddr_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x10);
> +
> + hws[LOONGSON2_DC_PLL] = loongson2_clk_pll_register("dc_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x20);
> +
> + hws[LOONGSON2_PIX0_PLL] = loongson2_clk_pll_register("pix0_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x30);
> +
> + hws[LOONGSON2_PIX1_PLL] = loongson2_clk_pll_register("pix1_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x40);
> +
> + 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(NULL, "apb",
> + "gmac",
> + &loongson2_apb_clk_ops, 0);
> +
> + hws[LOONGSON2_USB_CLK] = loongson2_clk_register(NULL, "usb",
> + "gmac",
> + &loongson2_usb_clk_ops, 0);
> +
> + hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(NULL, "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);
> +
above this, it may be more common to use macro defintions instead of
magic number. the clock configuration logic of 2k500 is same as 2k1000,
only the base and offset are different
> + loongson2_check_clk_hws(hws, LOONGSON2_CLK_END);
> +
> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
> +
> +err:
> + iounmap(loongson2_pll_base);
> +}
> +
> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
在 2022/11/30 11:27, XiaochuanMao 写道:
>
> hi, yinbo
>
> On 2022/11/29 11:41, Yinbo Zhu wrote:
>> 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<[email protected]>
>> ---
>> 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 +
>> arch/loongarch/Kconfig | 1 +
>> drivers/clk/Kconfig | 9 ++
>> drivers/clk/Makefile | 1 +
>> drivers/clk/clk-loongson2.c | 286 ++++++++++++++++++++++++++++++++++++
>> 5 files changed, 298 insertions(+)
>> create mode 100644 drivers/clk/clk-loongson2.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index ab94893fe2f6..73fa56f1fd5d 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -12025,6 +12025,7 @@ LOONGSON-2 SOC SERIES CLOCK DRIVER
>> M: Yinbo Zhu<[email protected]>
>> L: [email protected]
>> S: Maintained
>> +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/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
>> index 903096bd87f8..4f8f1b8f796d 100644
>> --- a/arch/loongarch/Kconfig
>> +++ b/arch/loongarch/Kconfig
>> @@ -127,6 +127,7 @@ config LOONGARCH
>> select USE_PERCPU_NUMA_NODE_ID
>> select USER_STACKTRACE_SUPPORT
>> select ZONE_DMA32
>> + select COMMON_CLK
>>
>> config 32BIT
>> bool
>> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
>> index d79905f3e174..d13626f63739 100644
>> --- a/drivers/clk/Kconfig
>> +++ b/drivers/clk/Kconfig
>> @@ -326,6 +326,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 COMMON_CLK && OF
>> + help
>> + This driver provides support for Clock Controller that base on
>> + Common Clock Framework Controller (CCF) on Loongson-2 SoC. The
>> + Clock Controller can generates and supplies clock to various
>> + peripherals within the SoC.
>> +
>> 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..7487effceeab
>> --- /dev/null
>> +++ b/drivers/clk/clk-loongson2.c
>> @@ -0,0 +1,286 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Author: Yinbo Zhu<[email protected]>
>> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
>> + */
>> +
>> +#include <linux/clkdev.h>
>> +#include <linux/err.h>
>> +#include <linux/init.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/slab.h>
>> +#include <linux/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
>> +
>> +static void __iomem *loongson2_pll_base;
>> +
>> +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 = kzalloc(sizeof(*hw), GFP_KERNEL);
>> + if (!hw)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + init.name = name;
>> + init.ops = ops;
>> + init.flags = flags;
>> + init.parent_names = (parent_name ? &parent_name : NULL);
>> + init.num_parents = (parent_name ? 1 : 0);
>> + hw->init = &init;
>> +
>> + /* register the clock */
>> + ret = clk_hw_register(dev, hw);
>> + if (ret) {
>> + kfree(hw);
>> + hw = ERR_PTR(ret);
>> + }
>> +
>> + return hw;
>> +}
>> +
>> +static struct clk_hw *loongson2_clk_pll_register(const char *name,
>> + const char *parent, void __iomem *reg)
>> +{
>> + u64 val;
>> + u32 mult = 1, div = 1;
>> +
>> + val = readq((void *)reg);
>> +
>> + 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 clk_hw_register_fixed_factor(NULL, name, parent,
>> + CLK_SET_RATE_PARENT, mult, div);
>> +}
>> +
>> +static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
>> + unsigned long parent_rate)
>> +{
>> + u64 val;
>> + u32 mult;
>> + unsigned long rate;
>> +
>> + val = readq((void *)(loongson2_pll_base + 0x50));
>> +
> it may be more common to use macro defintions instead of magic number.
> the clock configuration logic of 2k500 is same as 2k1000, only the
> base and offset are different。
<https://fanyi.baidu.com/###>
In addition to address differences, there are others, but according to
internal requirements,
2k1000 is considered first. 2k500 clock will be added later use other
compatibility method in
this driver.
>> + mult = (val >> LOONGSON2_APB_FREQSCALE_SHIFT) &
>> + clk_div_mask(LOONGSON2_APB_FREQSCALE_WIDTH);
>> +
>> + rate = parent_rate * (mult + 1);
>> + do_div(rate, 8);
>> +
>> + return rate;
>> +}
>> +
>> +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)
>> +{
>> + u64 val;
>> + u32 mult;
>> + unsigned long rate;
>> +
>> + val = readq((void *)(loongson2_pll_base + 0x50));
>> +
>> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
>> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
>> +
>> + rate = parent_rate * (mult + 1);
>> + do_div(rate, 8);
>> +
>> + return rate;
>> +}
>> +
>> +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)
>> +{
>> + u64 val;
>> + u32 mult;
>> + unsigned long rate;
>> +
>> + val = readq((void *)(loongson2_pll_base + 0x50));
>> +
>> + mult = (val >> LOONGSON2_SATA_FREQSCALE_SHIFT) &
>> + clk_div_mask(LOONGSON2_SATA_FREQSCALE_WIDTH);
>> +
>> + rate = parent_rate * (mult + 1);
>> + do_div(rate, 8);
>> +
>> + return rate;
>> +}
>> +
>> +static const struct clk_ops loongson2_sata_clk_ops = {
>> + .recalc_rate = loongson2_sata_recalc_rate,
>> +};
>> +
>> +static 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 struct clk_hw *loongson2_obtain_fixed_clk_hw(
>> + struct device_node *np,
>> + const char *name)
>> +{
>> + struct clk *clk;
>> +
>> + clk = of_clk_get_by_name(np, name);
>> + if (IS_ERR(clk))
>> + return ERR_PTR(-ENOENT);
>> +
>> + return __clk_get_hw(clk);
>> +}
>> +
>> +static void __init loongson2_clocks_init(struct device_node *np)
>> +{
>> + struct clk_hw **hws;
>> + struct clk_hw_onecell_data *clk_hw_data;
>> + spinlock_t loongson2_clk_lock;
>> +
>> + loongson2_pll_base = of_iomap(np, 0);
>> +
>> + if (!loongson2_pll_base) {
>> + pr_err("clk: unable to map loongson2 clk registers\n");
>> + goto err;
>> + }
>> +
>> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
>> + GFP_KERNEL);
>> + if (WARN_ON(!clk_hw_data))
>> + goto err;
>> +
>> + clk_hw_data->num = LOONGSON2_CLK_END;
>> + hws = clk_hw_data->hws;
>> +
>> + hws[LOONGSON2_REF_100M] = loongson2_obtain_fixed_clk_hw(np,
>> + "ref_100m");
>> +
>> + hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
>> + "ref_100m",
>> + loongson2_pll_base);
>> +
>> + hws[LOONGSON2_DDR_PLL] = loongson2_clk_pll_register("ddr_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x10);
>> +
>> + hws[LOONGSON2_DC_PLL] = loongson2_clk_pll_register("dc_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x20);
>> +
>> + hws[LOONGSON2_PIX0_PLL] = loongson2_clk_pll_register("pix0_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x30);
>> +
>> + hws[LOONGSON2_PIX1_PLL] = loongson2_clk_pll_register("pix1_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x40);
>> +
>> + 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(NULL, "apb",
>> + "gmac",
>> + &loongson2_apb_clk_ops, 0);
>> +
>> + hws[LOONGSON2_USB_CLK] = loongson2_clk_register(NULL, "usb",
>> + "gmac",
>> + &loongson2_usb_clk_ops, 0);
>> +
>> + hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(NULL, "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);
> above this, it may be more common to use macro defintions instead of
> magic number. the clock configuration logic of 2k500 is same as
> 2k1000, only the base and offset are different。
clk_hw_register_divider is a commone interface, I think
>> +
>> + loongson2_check_clk_hws(hws, LOONGSON2_CLK_END);
>> +
>> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
>> +
>> +err:
>> + iounmap(loongson2_pll_base);
>> +}
>> +
>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
>
>
> thanks
>
hi yinbo
On 2022/11/29 11:41, Yinbo Zhu wrote:
> 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 <[email protected]>
> ---
> 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 +
> arch/loongarch/Kconfig | 1 +
> drivers/clk/Kconfig | 9 ++
> drivers/clk/Makefile | 1 +
> drivers/clk/clk-loongson2.c | 286 ++++++++++++++++++++++++++++++++++++
> 5 files changed, 298 insertions(+)
> create mode 100644 drivers/clk/clk-loongson2.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ab94893fe2f6..73fa56f1fd5d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12025,6 +12025,7 @@ LOONGSON-2 SOC SERIES CLOCK DRIVER
> M: Yinbo Zhu <[email protected]>
> L: [email protected]
> S: Maintained
> +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/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 903096bd87f8..4f8f1b8f796d 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -127,6 +127,7 @@ config LOONGARCH
> select USE_PERCPU_NUMA_NODE_ID
> select USER_STACKTRACE_SUPPORT
> select ZONE_DMA32
> + select COMMON_CLK
>
> config 32BIT
> bool
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index d79905f3e174..d13626f63739 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -326,6 +326,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 COMMON_CLK && OF
> + help
> + This driver provides support for Clock Controller that base on
> + Common Clock Framework Controller (CCF) on Loongson-2 SoC. The
> + Clock Controller can generates and supplies clock to various
> + peripherals within the SoC.
> +
> 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..7487effceeab
> --- /dev/null
> +++ b/drivers/clk/clk-loongson2.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Author: Yinbo Zhu <[email protected]>
> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/clkdev.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/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
> +
> +static void __iomem *loongson2_pll_base;
> +
> +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 = kzalloc(sizeof(*hw), GFP_KERNEL);
> + if (!hw)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = ops;
> + init.flags = flags;
> + init.parent_names = (parent_name ? &parent_name : NULL);
> + init.num_parents = (parent_name ? 1 : 0);
> + hw->init = &init;
> +
> + /* register the clock */
> + ret = clk_hw_register(dev, hw);
> + if (ret) {
> + kfree(hw);
> + hw = ERR_PTR(ret);
> + }
> +
> + return hw;
> +}
> +
> +static struct clk_hw *loongson2_clk_pll_register(const char *name,
> + const char *parent, void __iomem *reg)
> +{
> + u64 val;
> + u32 mult = 1, div = 1;
> +
> + val = readq((void *)reg);
> +
> + 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 clk_hw_register_fixed_factor(NULL, name, parent,
> + CLK_SET_RATE_PARENT, mult, div);
> +}
> +
> +static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_APB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_APB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_SATA_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_SATA_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
loongson2_sata_recalc_rate, loongson2_usb_recalc_rate,loongson2_apb_recalc_rate are have the same calcuate logic code, only the shift and width are different,
I thought it would be possible to abstract out a common function to reduce duplicate code
> +static const struct clk_ops loongson2_sata_clk_ops = {
> + .recalc_rate = loongson2_sata_recalc_rate,
> +};
> +
> +static 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 struct clk_hw *loongson2_obtain_fixed_clk_hw(
> + struct device_node *np,
> + const char *name)
> +{
> + struct clk *clk;
> +
> + clk = of_clk_get_by_name(np, name);
> + if (IS_ERR(clk))
> + return ERR_PTR(-ENOENT);
> +
> + return __clk_get_hw(clk);
> +}
> +
> +static void __init loongson2_clocks_init(struct device_node *np)
> +{
> + struct clk_hw **hws;
> + struct clk_hw_onecell_data *clk_hw_data;
> + spinlock_t loongson2_clk_lock;
> +
> + loongson2_pll_base = of_iomap(np, 0);
> +
> + if (!loongson2_pll_base) {
> + pr_err("clk: unable to map loongson2 clk registers\n");
> + goto err;
> + }
> +
> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
> + GFP_KERNEL);
> + if (WARN_ON(!clk_hw_data))
> + goto err;
> +
> + clk_hw_data->num = LOONGSON2_CLK_END;
> + hws = clk_hw_data->hws;
> +
> + hws[LOONGSON2_REF_100M] = loongson2_obtain_fixed_clk_hw(np,
> + "ref_100m");
> +
> + hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
> + "ref_100m",
> + loongson2_pll_base);
> +
> + hws[LOONGSON2_DDR_PLL] = loongson2_clk_pll_register("ddr_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x10);
> +
> + hws[LOONGSON2_DC_PLL] = loongson2_clk_pll_register("dc_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x20);
> +
> + hws[LOONGSON2_PIX0_PLL] = loongson2_clk_pll_register("pix0_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x30);
> +
> + hws[LOONGSON2_PIX1_PLL] = loongson2_clk_pll_register("pix1_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x40);
> +
> + 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(NULL, "apb",
> + "gmac",
> + &loongson2_apb_clk_ops, 0);
> +
> + hws[LOONGSON2_USB_CLK] = loongson2_clk_register(NULL, "usb",
> + "gmac",
> + &loongson2_usb_clk_ops, 0);
> +
> + hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(NULL, "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);
> +
> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
> +
> +err:
> + iounmap(loongson2_pll_base);
> +}
> +
> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
在 2022/11/30 21:14, XiaochuanMao 写道:
> hi yinbo
>
> On 2022/11/29 11:41, Yinbo Zhu wrote:
>> 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 <[email protected]>
>> ---
>> 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 +
>> arch/loongarch/Kconfig | 1 +
>> drivers/clk/Kconfig | 9 ++
>> drivers/clk/Makefile | 1 +
>> drivers/clk/clk-loongson2.c | 286 ++++++++++++++++++++++++++++++++++++
>> 5 files changed, 298 insertions(+)
>> create mode 100644 drivers/clk/clk-loongson2.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index ab94893fe2f6..73fa56f1fd5d 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -12025,6 +12025,7 @@ LOONGSON-2 SOC SERIES CLOCK DRIVER
>> M: Yinbo Zhu <[email protected]>
>> L: [email protected]
>> S: Maintained
>> +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/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
>> index 903096bd87f8..4f8f1b8f796d 100644
>> --- a/arch/loongarch/Kconfig
>> +++ b/arch/loongarch/Kconfig
>> @@ -127,6 +127,7 @@ config LOONGARCH
>> select USE_PERCPU_NUMA_NODE_ID
>> select USER_STACKTRACE_SUPPORT
>> select ZONE_DMA32
>> + select COMMON_CLK
>> config 32BIT
>> bool
>> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
>> index d79905f3e174..d13626f63739 100644
>> --- a/drivers/clk/Kconfig
>> +++ b/drivers/clk/Kconfig
>> @@ -326,6 +326,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 COMMON_CLK && OF
>> + help
>> + This driver provides support for Clock Controller that base on
>> + Common Clock Framework Controller (CCF) on Loongson-2 SoC. The
>> + Clock Controller can generates and supplies clock to various
>> + peripherals within the SoC.
>> +
>> 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..7487effceeab
>> --- /dev/null
>> +++ b/drivers/clk/clk-loongson2.c
>> @@ -0,0 +1,286 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Author: Yinbo Zhu <[email protected]>
>> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
>> + */
>> +
>> +#include <linux/clkdev.h>
>> +#include <linux/err.h>
>> +#include <linux/init.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/slab.h>
>> +#include <linux/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
>> +
>> +static void __iomem *loongson2_pll_base;
>> +
>> +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 = kzalloc(sizeof(*hw), GFP_KERNEL);
>> + if (!hw)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + init.name = name;
>> + init.ops = ops;
>> + init.flags = flags;
>> + init.parent_names = (parent_name ? &parent_name : NULL);
>> + init.num_parents = (parent_name ? 1 : 0);
>> + hw->init = &init;
>> +
>> + /* register the clock */
>> + ret = clk_hw_register(dev, hw);
>> + if (ret) {
>> + kfree(hw);
>> + hw = ERR_PTR(ret);
>> + }
>> +
>> + return hw;
>> +}
>> +
>> +static struct clk_hw *loongson2_clk_pll_register(const char *name,
>> + const char *parent, void __iomem *reg)
>> +{
>> + u64 val;
>> + u32 mult = 1, div = 1;
>> +
>> + val = readq((void *)reg);
>> +
>> + 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 clk_hw_register_fixed_factor(NULL, name, parent,
>> + CLK_SET_RATE_PARENT, mult, div);
>> +}
>> +
>> +static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
>> + unsigned long parent_rate)
>> +{
>> + u64 val;
>> + u32 mult;
>> + unsigned long rate;
>> +
>> + val = readq((void *)(loongson2_pll_base + 0x50));
>> +
>> + mult = (val >> LOONGSON2_APB_FREQSCALE_SHIFT) &
>> + clk_div_mask(LOONGSON2_APB_FREQSCALE_WIDTH);
>> +
>> + rate = parent_rate * (mult + 1);
>> + do_div(rate, 8);
>> +
>> + return rate;
>> +}
>> +
>> +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)
>> +{
>> + u64 val;
>> + u32 mult;
>> + unsigned long rate;
>> +
>> + val = readq((void *)(loongson2_pll_base + 0x50));
>> +
>> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
>> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
>> +
>> + rate = parent_rate * (mult + 1);
>> + do_div(rate, 8);
>> +
>> + return rate;
>> +}
>> +
>> +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)
>> +{
>> + u64 val;
>> + u32 mult;
>> + unsigned long rate;
>> +
>> + val = readq((void *)(loongson2_pll_base + 0x50));
>> +
>> + mult = (val >> LOONGSON2_SATA_FREQSCALE_SHIFT) &
>> + clk_div_mask(LOONGSON2_SATA_FREQSCALE_WIDTH);
>> +
>> + rate = parent_rate * (mult + 1);
>> + do_div(rate, 8);
>> +
>> + return rate;
>> +}
>> +
>
> loongson2_sata_recalc_rate,
> loongson2_usb_recalc_rate,loongson2_apb_recalc_rate are have the same
> calcuate logic code, only the shift and width are different,
> I thought it would be possible to abstract out a common function to
> reduce duplicate code
Thansk your advice, they are base on the same logic, I have noticed this
at the begining,
but I don't abstract it because the parameter of recalc_rate is fixed,
and no other parameter
can be passed into it.
in fact, at the v1 version, I wanted to use a common interface to
replace the logic of specific
implemetion,but the calculation of this clock was not standard, so I had
to do this.
>
>> +static const struct clk_ops loongson2_sata_clk_ops = {
>> + .recalc_rate = loongson2_sata_recalc_rate,
>> +};
>> +
>> +static 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 struct clk_hw *loongson2_obtain_fixed_clk_hw(
>> + struct device_node *np,
>> + const char *name)
>> +{
>> + struct clk *clk;
>> +
>> + clk = of_clk_get_by_name(np, name);
>> + if (IS_ERR(clk))
>> + return ERR_PTR(-ENOENT);
>> +
>> + return __clk_get_hw(clk);
>> +}
>> +
>> +static void __init loongson2_clocks_init(struct device_node *np)
>> +{
>> + struct clk_hw **hws;
>> + struct clk_hw_onecell_data *clk_hw_data;
>> + spinlock_t loongson2_clk_lock;
>> +
>> + loongson2_pll_base = of_iomap(np, 0);
>> +
>> + if (!loongson2_pll_base) {
>> + pr_err("clk: unable to map loongson2 clk registers\n");
>> + goto err;
>> + }
>> +
>> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws,
>> LOONGSON2_CLK_END),
>> + GFP_KERNEL);
>> + if (WARN_ON(!clk_hw_data))
>> + goto err;
>> +
>> + clk_hw_data->num = LOONGSON2_CLK_END;
>> + hws = clk_hw_data->hws;
>> +
>> + hws[LOONGSON2_REF_100M] = loongson2_obtain_fixed_clk_hw(np,
>> + "ref_100m");
>> +
>> + hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
>> + "ref_100m",
>> + loongson2_pll_base);
>> +
>> + hws[LOONGSON2_DDR_PLL] = loongson2_clk_pll_register("ddr_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x10);
>> +
>> + hws[LOONGSON2_DC_PLL] = loongson2_clk_pll_register("dc_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x20);
>> +
>> + hws[LOONGSON2_PIX0_PLL] = loongson2_clk_pll_register("pix0_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x30);
>> +
>> + hws[LOONGSON2_PIX1_PLL] = loongson2_clk_pll_register("pix1_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x40);
>> +
>> + 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(NULL, "apb",
>> + "gmac",
>> + &loongson2_apb_clk_ops, 0);
>> +
>> + hws[LOONGSON2_USB_CLK] = loongson2_clk_register(NULL, "usb",
>> + "gmac",
>> + &loongson2_usb_clk_ops, 0);
>> +
>> + hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(NULL, "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);
>> +
>> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
>> +
>> +err:
>> + iounmap(loongson2_pll_base);
>> +}
>> +
>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk",
>> loongson2_clocks_init);
Hi clock maintainer,
Could you help me merge this series patch.
Thanks.
在 2022/11/29 11:41, Yinbo Zhu 写道:
> 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 <[email protected]>
> ---
> 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 +
> arch/loongarch/Kconfig | 1 +
> drivers/clk/Kconfig | 9 ++
> drivers/clk/Makefile | 1 +
> drivers/clk/clk-loongson2.c | 286 ++++++++++++++++++++++++++++++++++++
> 5 files changed, 298 insertions(+)
> create mode 100644 drivers/clk/clk-loongson2.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ab94893fe2f6..73fa56f1fd5d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12025,6 +12025,7 @@ LOONGSON-2 SOC SERIES CLOCK DRIVER
> M: Yinbo Zhu <[email protected]>
> L: [email protected]
> S: Maintained
> +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/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 903096bd87f8..4f8f1b8f796d 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -127,6 +127,7 @@ config LOONGARCH
> select USE_PERCPU_NUMA_NODE_ID
> select USER_STACKTRACE_SUPPORT
> select ZONE_DMA32
> + select COMMON_CLK
>
> config 32BIT
> bool
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index d79905f3e174..d13626f63739 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -326,6 +326,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 COMMON_CLK && OF
> + help
> + This driver provides support for Clock Controller that base on
> + Common Clock Framework Controller (CCF) on Loongson-2 SoC. The
> + Clock Controller can generates and supplies clock to various
> + peripherals within the SoC.
> +
> 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..7487effceeab
> --- /dev/null
> +++ b/drivers/clk/clk-loongson2.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Author: Yinbo Zhu <[email protected]>
> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/clkdev.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/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
> +
> +static void __iomem *loongson2_pll_base;
> +
> +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 = kzalloc(sizeof(*hw), GFP_KERNEL);
> + if (!hw)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = ops;
> + init.flags = flags;
> + init.parent_names = (parent_name ? &parent_name : NULL);
> + init.num_parents = (parent_name ? 1 : 0);
> + hw->init = &init;
> +
> + /* register the clock */
> + ret = clk_hw_register(dev, hw);
> + if (ret) {
> + kfree(hw);
> + hw = ERR_PTR(ret);
> + }
> +
> + return hw;
> +}
> +
> +static struct clk_hw *loongson2_clk_pll_register(const char *name,
> + const char *parent, void __iomem *reg)
> +{
> + u64 val;
> + u32 mult = 1, div = 1;
> +
> + val = readq((void *)reg);
> +
> + 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 clk_hw_register_fixed_factor(NULL, name, parent,
> + CLK_SET_RATE_PARENT, mult, div);
> +}
> +
> +static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_APB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_APB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_SATA_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_SATA_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +static const struct clk_ops loongson2_sata_clk_ops = {
> + .recalc_rate = loongson2_sata_recalc_rate,
> +};
> +
> +static 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 struct clk_hw *loongson2_obtain_fixed_clk_hw(
> + struct device_node *np,
> + const char *name)
> +{
> + struct clk *clk;
> +
> + clk = of_clk_get_by_name(np, name);
> + if (IS_ERR(clk))
> + return ERR_PTR(-ENOENT);
> +
> + return __clk_get_hw(clk);
> +}
> +
> +static void __init loongson2_clocks_init(struct device_node *np)
> +{
> + struct clk_hw **hws;
> + struct clk_hw_onecell_data *clk_hw_data;
> + spinlock_t loongson2_clk_lock;
> +
> + loongson2_pll_base = of_iomap(np, 0);
> +
> + if (!loongson2_pll_base) {
> + pr_err("clk: unable to map loongson2 clk registers\n");
> + goto err;
> + }
> +
> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
> + GFP_KERNEL);
> + if (WARN_ON(!clk_hw_data))
> + goto err;
> +
> + clk_hw_data->num = LOONGSON2_CLK_END;
> + hws = clk_hw_data->hws;
> +
> + hws[LOONGSON2_REF_100M] = loongson2_obtain_fixed_clk_hw(np,
> + "ref_100m");
> +
> + hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
> + "ref_100m",
> + loongson2_pll_base);
> +
> + hws[LOONGSON2_DDR_PLL] = loongson2_clk_pll_register("ddr_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x10);
> +
> + hws[LOONGSON2_DC_PLL] = loongson2_clk_pll_register("dc_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x20);
> +
> + hws[LOONGSON2_PIX0_PLL] = loongson2_clk_pll_register("pix0_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x30);
> +
> + hws[LOONGSON2_PIX1_PLL] = loongson2_clk_pll_register("pix1_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x40);
> +
> + 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(NULL, "apb",
> + "gmac",
> + &loongson2_apb_clk_ops, 0);
> +
> + hws[LOONGSON2_USB_CLK] = loongson2_clk_register(NULL, "usb",
> + "gmac",
> + &loongson2_usb_clk_ops, 0);
> +
> + hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(NULL, "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);
> +
> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
> +
> +err:
> + iounmap(loongson2_pll_base);
> +}
> +
> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
Friendly ping ?
?? 2022/11/29 ????11:41, Yinbo Zhu д??:
> 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 <[email protected]>
> ---
> 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 +
> arch/loongarch/Kconfig | 1 +
> drivers/clk/Kconfig | 9 ++
> drivers/clk/Makefile | 1 +
> drivers/clk/clk-loongson2.c | 286 ++++++++++++++++++++++++++++++++++++
> 5 files changed, 298 insertions(+)
> create mode 100644 drivers/clk/clk-loongson2.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ab94893fe2f6..73fa56f1fd5d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12025,6 +12025,7 @@ LOONGSON-2 SOC SERIES CLOCK DRIVER
> M: Yinbo Zhu <[email protected]>
> L: [email protected]
> S: Maintained
> +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/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 903096bd87f8..4f8f1b8f796d 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -127,6 +127,7 @@ config LOONGARCH
> select USE_PERCPU_NUMA_NODE_ID
> select USER_STACKTRACE_SUPPORT
> select ZONE_DMA32
> + select COMMON_CLK
>
> config 32BIT
> bool
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index d79905f3e174..d13626f63739 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -326,6 +326,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 COMMON_CLK && OF
> + help
> + This driver provides support for Clock Controller that base on
> + Common Clock Framework Controller (CCF) on Loongson-2 SoC. The
> + Clock Controller can generates and supplies clock to various
> + peripherals within the SoC.
> +
> 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..7487effceeab
> --- /dev/null
> +++ b/drivers/clk/clk-loongson2.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Author: Yinbo Zhu <[email protected]>
> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/clkdev.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/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
> +
> +static void __iomem *loongson2_pll_base;
> +
> +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 = kzalloc(sizeof(*hw), GFP_KERNEL);
> + if (!hw)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = ops;
> + init.flags = flags;
> + init.parent_names = (parent_name ? &parent_name : NULL);
> + init.num_parents = (parent_name ? 1 : 0);
> + hw->init = &init;
> +
> + /* register the clock */
> + ret = clk_hw_register(dev, hw);
> + if (ret) {
> + kfree(hw);
> + hw = ERR_PTR(ret);
> + }
> +
> + return hw;
> +}
> +
> +static struct clk_hw *loongson2_clk_pll_register(const char *name,
> + const char *parent, void __iomem *reg)
> +{
> + u64 val;
> + u32 mult = 1, div = 1;
> +
> + val = readq((void *)reg);
> +
> + 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 clk_hw_register_fixed_factor(NULL, name, parent,
> + CLK_SET_RATE_PARENT, mult, div);
> +}
> +
> +static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_APB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_APB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
> +
> + mult = (val >> LOONGSON2_SATA_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_SATA_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +static const struct clk_ops loongson2_sata_clk_ops = {
> + .recalc_rate = loongson2_sata_recalc_rate,
> +};
> +
> +static 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 struct clk_hw *loongson2_obtain_fixed_clk_hw(
> + struct device_node *np,
> + const char *name)
> +{
> + struct clk *clk;
> +
> + clk = of_clk_get_by_name(np, name);
> + if (IS_ERR(clk))
> + return ERR_PTR(-ENOENT);
> +
> + return __clk_get_hw(clk);
> +}
> +
> +static void __init loongson2_clocks_init(struct device_node *np)
> +{
> + struct clk_hw **hws;
> + struct clk_hw_onecell_data *clk_hw_data;
> + spinlock_t loongson2_clk_lock;
> +
> + loongson2_pll_base = of_iomap(np, 0);
> +
> + if (!loongson2_pll_base) {
> + pr_err("clk: unable to map loongson2 clk registers\n");
> + goto err;
> + }
> +
> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
> + GFP_KERNEL);
> + if (WARN_ON(!clk_hw_data))
> + goto err;
> +
> + clk_hw_data->num = LOONGSON2_CLK_END;
> + hws = clk_hw_data->hws;
> +
> + hws[LOONGSON2_REF_100M] = loongson2_obtain_fixed_clk_hw(np,
> + "ref_100m");
> +
> + hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
> + "ref_100m",
> + loongson2_pll_base);
> +
> + hws[LOONGSON2_DDR_PLL] = loongson2_clk_pll_register("ddr_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x10);
> +
> + hws[LOONGSON2_DC_PLL] = loongson2_clk_pll_register("dc_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x20);
> +
> + hws[LOONGSON2_PIX0_PLL] = loongson2_clk_pll_register("pix0_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x30);
> +
> + hws[LOONGSON2_PIX1_PLL] = loongson2_clk_pll_register("pix1_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x40);
> +
> + 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(NULL, "apb",
> + "gmac",
> + &loongson2_apb_clk_ops, 0);
> +
> + hws[LOONGSON2_USB_CLK] = loongson2_clk_register(NULL, "usb",
> + "gmac",
> + &loongson2_usb_clk_ops, 0);
> +
> + hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(NULL, "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);
> +
> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
> +
> +err:
> + iounmap(loongson2_pll_base);
> +}
> +
> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
Quoting Yinbo Zhu (2022-11-28 19:41:55)
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 903096bd87f8..4f8f1b8f796d 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -127,6 +127,7 @@ config LOONGARCH
> select USE_PERCPU_NUMA_NODE_ID
> select USER_STACKTRACE_SUPPORT
> select ZONE_DMA32
> + select COMMON_CLK
Any reason to put this at the end instead of alphabetically?
>
> config 32BIT
> bool
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index d79905f3e174..d13626f63739 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -326,6 +326,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 COMMON_CLK && OF
> + help
> + This driver provides support for Clock Controller that base on
Don't capitalize 'Clock Controller'
> + Common Clock Framework Controller (CCF) on Loongson-2 SoC. The
CCF doesn't really matter. Just say "support for clock controller on
Loongson-2 SoC"
> + Clock Controller can generates and supplies clock to various
Same capitalization comment.
> + peripherals within the SoC.
> +
> config COMMON_CLK_NXP
> def_bool COMMON_CLK && (ARCH_LPC18XX || ARCH_LPC32XX)
> select REGMAP_MMIO if ARCH_LPC32XX
> diff --git a/drivers/clk/clk-loongson2.c b/drivers/clk/clk-loongson2.c
> new file mode 100644
> index 000000000000..7487effceeab
> --- /dev/null
> +++ b/drivers/clk/clk-loongson2.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Author: Yinbo Zhu <[email protected]>
> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/clkdev.h>
Drop include that isn't used.
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/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
> +
> +static void __iomem *loongson2_pll_base;
> +
> +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 = kzalloc(sizeof(*hw), GFP_KERNEL);
> + if (!hw)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = ops;
> + init.flags = flags;
> + init.parent_names = (parent_name ? &parent_name : NULL);
> + init.num_parents = (parent_name ? 1 : 0);
> + hw->init = &init;
> +
> + /* register the clock */
> + ret = clk_hw_register(dev, hw);
> + if (ret) {
> + kfree(hw);
> + hw = ERR_PTR(ret);
> + }
> +
> + return hw;
> +}
> +
> +static struct clk_hw *loongson2_clk_pll_register(const char *name,
> + const char *parent, void __iomem *reg)
> +{
> + u64 val;
> + u32 mult = 1, div = 1;
> +
> + val = readq((void *)reg);
Drop the cast.
> +
> + 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 clk_hw_register_fixed_factor(NULL, name, parent,
> + CLK_SET_RATE_PARENT, mult, div);
> +}
> +
> +static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
Drop the cast.
> +
> + mult = (val >> LOONGSON2_APB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_APB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
Drop the cast.
> +
> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
Why is do_div() being used?
> +
> + return rate;
> +}
> +
> +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)
> +{
> + u64 val;
> + u32 mult;
> + unsigned long rate;
> +
> + val = readq((void *)(loongson2_pll_base + 0x50));
Drop the cast.
> +
> + mult = (val >> LOONGSON2_SATA_FREQSCALE_SHIFT) &
> + clk_div_mask(LOONGSON2_SATA_FREQSCALE_WIDTH);
> +
> + rate = parent_rate * (mult + 1);
> + do_div(rate, 8);
> +
> + return rate;
> +}
> +
> +static const struct clk_ops loongson2_sata_clk_ops = {
> + .recalc_rate = loongson2_sata_recalc_rate,
> +};
> +
> +static void loongson2_check_clk_hws(struct clk_hw *clks[], unsigned int count)
Inline this function.
> +{
> + 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]));
Put the comma on the line above please.
> +}
> +
> +static struct clk_hw *loongson2_obtain_fixed_clk_hw(
> + struct device_node *np,
> + const char *name)
> +{
> + struct clk *clk;
> +
> + clk = of_clk_get_by_name(np, name);
> + if (IS_ERR(clk))
> + return ERR_PTR(-ENOENT);
> +
> + return __clk_get_hw(clk);
Please don't implement this whole function. Instead, use clk_parent_data
and the 'fw_name' or 'index' members to assign clk parents.
> +}
> +
> +static void __init loongson2_clocks_init(struct device_node *np)
> +{
> + struct clk_hw **hws;
> + struct clk_hw_onecell_data *clk_hw_data;
> + spinlock_t loongson2_clk_lock;
> +
> + loongson2_pll_base = of_iomap(np, 0);
> +
> + if (!loongson2_pll_base) {
> + pr_err("clk: unable to map loongson2 clk registers\n");
> + goto err;
return?
> + }
> +
> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
> + GFP_KERNEL);
> + if (WARN_ON(!clk_hw_data))
> + goto err;
> +
> + clk_hw_data->num = LOONGSON2_CLK_END;
> + hws = clk_hw_data->hws;
> +
> + hws[LOONGSON2_REF_100M] = loongson2_obtain_fixed_clk_hw(np,
> + "ref_100m");
> +
> + hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
> + "ref_100m",
> + loongson2_pll_base);
> +
> + hws[LOONGSON2_DDR_PLL] = loongson2_clk_pll_register("ddr_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x10);
> +
> + hws[LOONGSON2_DC_PLL] = loongson2_clk_pll_register("dc_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x20);
> +
> + hws[LOONGSON2_PIX0_PLL] = loongson2_clk_pll_register("pix0_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x30);
> +
> + hws[LOONGSON2_PIX1_PLL] = loongson2_clk_pll_register("pix1_pll",
> + "ref_100m",
> + loongson2_pll_base + 0x40);
> +
> + 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(NULL, "apb",
> + "gmac",
> + &loongson2_apb_clk_ops, 0);
> +
> + hws[LOONGSON2_USB_CLK] = loongson2_clk_register(NULL, "usb",
> + "gmac",
> + &loongson2_usb_clk_ops, 0);
> +
> + hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(NULL, "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);
> +
> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
> +
> +err:
> + iounmap(loongson2_pll_base);
> +}
> +
> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
Any reason this can't be a platform driver?
在 2023/2/11 上午7:42, Stephen Boyd 写道:
> Quoting Yinbo Zhu (2022-11-28 19:41:55)
>> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
>> index 903096bd87f8..4f8f1b8f796d 100644
>> --- a/arch/loongarch/Kconfig
>> +++ b/arch/loongarch/Kconfig
>> @@ -127,6 +127,7 @@ config LOONGARCH
>> select USE_PERCPU_NUMA_NODE_ID
>> select USER_STACKTRACE_SUPPORT
>> select ZONE_DMA32
>> + select COMMON_CLK
> Any reason to put this at the end instead of alphabetically?
okay, I will change it as alphabetically.
>>
>> config 32BIT
>> bool
>> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
>> index d79905f3e174..d13626f63739 100644
>> --- a/drivers/clk/Kconfig
>> +++ b/drivers/clk/Kconfig
>> @@ -326,6 +326,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 COMMON_CLK && OF
>> + help
>> + This driver provides support for Clock Controller that base on
> Don't capitalize 'Clock Controller'
okay, I got it.
>
>> + Common Clock Framework Controller (CCF) on Loongson-2 SoC. The
> CCF doesn't really matter. Just say "support for clock controller on
> Loongson-2 SoC"
okay, I got it.
>
>> + Clock Controller can generates and supplies clock to various
> Same capitalization comment.
okay, I got it.
>
>> + peripherals within the SoC.
>> +
>> config COMMON_CLK_NXP
>> def_bool COMMON_CLK && (ARCH_LPC18XX || ARCH_LPC32XX)
>> select REGMAP_MMIO if ARCH_LPC32XX
>> diff --git a/drivers/clk/clk-loongson2.c b/drivers/clk/clk-loongson2.c
>> new file mode 100644
>> index 000000000000..7487effceeab
>> --- /dev/null
>> +++ b/drivers/clk/clk-loongson2.c
>> @@ -0,0 +1,286 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Author: Yinbo Zhu <[email protected]>
>> + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
>> + */
>> +
>> +#include <linux/clkdev.h>
> Drop include that isn't used.
okay, I got it.
>
>> +#include <linux/err.h>
>> +#include <linux/init.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <dt-bindings/clock/loongson,ls2k-clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/slab.h>
>> +#include <linux/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
>> +
>> +static void __iomem *loongson2_pll_base;
>> +
>> +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 = kzalloc(sizeof(*hw), GFP_KERNEL);
>> + if (!hw)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + init.name = name;
>> + init.ops = ops;
>> + init.flags = flags;
>> + init.parent_names = (parent_name ? &parent_name : NULL);
>> + init.num_parents = (parent_name ? 1 : 0);
>> + hw->init = &init;
>> +
>> + /* register the clock */
>> + ret = clk_hw_register(dev, hw);
>> + if (ret) {
>> + kfree(hw);
>> + hw = ERR_PTR(ret);
>> + }
>> +
>> + return hw;
>> +}
>> +
>> +static struct clk_hw *loongson2_clk_pll_register(const char *name,
>> + const char *parent, void __iomem *reg)
>> +{
>> + u64 val;
>> + u32 mult = 1, div = 1;
>> +
>> + val = readq((void *)reg);
> Drop the cast.
I will remove the (void *).
>
>> +
>> + 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 clk_hw_register_fixed_factor(NULL, name, parent,
>> + CLK_SET_RATE_PARENT, mult, div);
>> +}
>> +
>> +static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
>> + unsigned long parent_rate)
>> +{
>> + u64 val;
>> + u32 mult;
>> + unsigned long rate;
>> +
>> + val = readq((void *)(loongson2_pll_base + 0x50));
> Drop the cast.
I will remove the (void *).
>
>> +
>> + mult = (val >> LOONGSON2_APB_FREQSCALE_SHIFT) &
>> + clk_div_mask(LOONGSON2_APB_FREQSCALE_WIDTH);
>> +
>> + rate = parent_rate * (mult + 1);
>> + do_div(rate, 8);
>> +
>> + return rate;
>> +}
>> +
>> +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)
>> +{
>> + u64 val;
>> + u32 mult;
>> + unsigned long rate;
>> +
>> + val = readq((void *)(loongson2_pll_base + 0x50));
> Drop the cast.
I will remove the (void *).
>
>> +
>> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
>> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
>> +
>> + rate = parent_rate * (mult + 1);
>> + do_div(rate, 8);
> Why is do_div() being used?
no expecial reason, I only want to get a result that rate divide 8.
>
>> +
>> + return rate;
>> +}
>> +
>> +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)
>> +{
>> + u64 val;
>> + u32 mult;
>> + unsigned long rate;
>> +
>> + val = readq((void *)(loongson2_pll_base + 0x50));
> Drop the cast.
I will remove the (void *).
>
>> +
>> + mult = (val >> LOONGSON2_SATA_FREQSCALE_SHIFT) &
>> + clk_div_mask(LOONGSON2_SATA_FREQSCALE_WIDTH);
>> +
>> + rate = parent_rate * (mult + 1);
>> + do_div(rate, 8);
>> +
>> + return rate;
>> +}
>> +
>> +static const struct clk_ops loongson2_sata_clk_ops = {
>> + .recalc_rate = loongson2_sata_recalc_rate,
>> +};
>> +
>> +static void loongson2_check_clk_hws(struct clk_hw *clks[], unsigned int count)
> Inline this function.
I will add 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]));
> Put the comma on the line above please.
okay, I got it.
>
>> +}
>> +
>> +static struct clk_hw *loongson2_obtain_fixed_clk_hw(
>> + struct device_node *np,
>> + const char *name)
>> +{
>> + struct clk *clk;
>> +
>> + clk = of_clk_get_by_name(np, name);
>> + if (IS_ERR(clk))
>> + return ERR_PTR(-ENOENT);
>> +
>> + return __clk_get_hw(clk);
> Please don't implement this whole function. Instead, use clk_parent_data
> and the 'fw_name' or 'index' members to assign clk parents.
at present, the child clk doesn't depend on the clk_parent_data, eg.
hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
"ref_100m", loongson2_pll_base);
static struct clk_hw *loongson2_clk_pll_register(const char *name,
const char *parent, void __iomem *reg)
{
u64 val;
u32 mult = 1, div = 1;
val = readq((void *)reg);
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 clk_hw_register_fixed_factor(NULL, name, parent,
CLK_SET_RATE_PARENT, mult, div);
}
you meaning is to use clk_parent_data to rework
loongson2_clk_pll_register and drop
loongson2_obtain_fixed_clk_hw ?
>
>> +}
>> +
>> +static void __init loongson2_clocks_init(struct device_node *np)
>> +{
>> + struct clk_hw **hws;
>> + struct clk_hw_onecell_data *clk_hw_data;
>> + spinlock_t loongson2_clk_lock;
>> +
>> + loongson2_pll_base = of_iomap(np, 0);
>> +
>> + if (!loongson2_pll_base) {
>> + pr_err("clk: unable to map loongson2 clk registers\n");
>> + goto err;
> return?
sorry, I don't get it. you meaning is that remove "goto err". Instead,
add a "return".
>
>> + }
>> +
>> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
>> + GFP_KERNEL);
>> + if (WARN_ON(!clk_hw_data))
>> + goto err;
>> +
>> + clk_hw_data->num = LOONGSON2_CLK_END;
>> + hws = clk_hw_data->hws;
>> +
>> + hws[LOONGSON2_REF_100M] = loongson2_obtain_fixed_clk_hw(np,
>> + "ref_100m");
>> +
>> + hws[LOONGSON2_NODE_PLL] = loongson2_clk_pll_register("node_pll",
>> + "ref_100m",
>> + loongson2_pll_base);
>> +
>> + hws[LOONGSON2_DDR_PLL] = loongson2_clk_pll_register("ddr_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x10);
>> +
>> + hws[LOONGSON2_DC_PLL] = loongson2_clk_pll_register("dc_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x20);
>> +
>> + hws[LOONGSON2_PIX0_PLL] = loongson2_clk_pll_register("pix0_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x30);
>> +
>> + hws[LOONGSON2_PIX1_PLL] = loongson2_clk_pll_register("pix1_pll",
>> + "ref_100m",
>> + loongson2_pll_base + 0x40);
>> +
>> + 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(NULL, "apb",
>> + "gmac",
>> + &loongson2_apb_clk_ops, 0);
>> +
>> + hws[LOONGSON2_USB_CLK] = loongson2_clk_register(NULL, "usb",
>> + "gmac",
>> + &loongson2_usb_clk_ops, 0);
>> +
>> + hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(NULL, "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);
>> +
>> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
>> +
>> +err:
>> + iounmap(loongson2_pll_base);
>> +}
>> +
>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
> Any reason this can't be a platform driver?
For the compatible consideration of other clock controllers of
Loongson-2 series in the future, the way of using dts can be
better compatible.
Quoting zhuyinbo (2023-02-14 23:35:22)
>
> 在 2023/2/11 上午7:42, Stephen Boyd 写道:
> > Quoting Yinbo Zhu (2022-11-28 19:41:55)
> >
> >> +
> >> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
> >> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
> >> +
> >> + rate = parent_rate * (mult + 1);
> >> + do_div(rate, 8);
> > Why is do_div() being used?
> no expecial reason, I only want to get a result that rate divide 8.
Ok, you can use div_u64() here and simplify.
> >
>
> you meaning is to use clk_parent_data to rework
> loongson2_clk_pll_register and drop
>
> loongson2_obtain_fixed_clk_hw ?
Yes
>
> >
> >> +}
> >> +
> >> +static void __init loongson2_clocks_init(struct device_node *np)
> >> +{
> >> + struct clk_hw **hws;
> >> + struct clk_hw_onecell_data *clk_hw_data;
> >> + spinlock_t loongson2_clk_lock;
> >> +
> >> + loongson2_pll_base = of_iomap(np, 0);
> >> +
> >> + if (!loongson2_pll_base) {
> >> + pr_err("clk: unable to map loongson2 clk registers\n");
> >> + goto err;
> > return?
> sorry, I don't get it. you meaning is that remove "goto err". Instead,
> add a "return".
Yes.
> >
> >> + }
> >> +
> >> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
> >> + GFP_KERNEL);
> >> + if (WARN_ON(!clk_hw_data))
> >> + goto err;
[...]
> >> +
> >> +err:
> >> + iounmap(loongson2_pll_base);
> >> +}
> >> +
> >> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
> > Any reason this can't be a platform driver?
>
> For the compatible consideration of other clock controllers of
> Loongson-2 series in the future, the way of using dts can be
>
> better compatible.
>
Sorry that sentence doesn't make sense to me. The use of dts doesn't
require the use of CLK_OF_DECLARE.
在 2023/2/18 上午6:15, Stephen Boyd 写道:
> Quoting zhuyinbo (2023-02-14 23:35:22)
>> 在 2023/2/11 上午7:42, Stephen Boyd 写道:
>>> Quoting Yinbo Zhu (2022-11-28 19:41:55)
>>>
>>>> +
>>>> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) &
>>>> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH);
>>>> +
>>>> + rate = parent_rate * (mult + 1);
>>>> + do_div(rate, 8);
>>> Why is do_div() being used?
>> no expecial reason, I only want to get a result that rate divide 8.
> Ok, you can use div_u64() here and simplify.
okay, I got it.
>
>> you meaning is to use clk_parent_data to rework
>> loongson2_clk_pll_register and drop
>>
>> loongson2_obtain_fixed_clk_hw ?
> Yes
>
>>>> +}
>>>> +
>>>> +static void __init loongson2_clocks_init(struct device_node *np)
>>>> +{
>>>> + struct clk_hw **hws;
>>>> + struct clk_hw_onecell_data *clk_hw_data;
>>>> + spinlock_t loongson2_clk_lock;
>>>> +
>>>> + loongson2_pll_base = of_iomap(np, 0);
>>>> +
>>>> + if (!loongson2_pll_base) {
>>>> + pr_err("clk: unable to map loongson2 clk registers\n");
>>>> + goto err;
>>> return?
>> sorry, I don't get it. you meaning is that remove "goto err". Instead,
>> add a "return".
> Yes.
>
>>>> + }
>>>> +
>>>> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
>>>> + GFP_KERNEL);
>>>> + if (WARN_ON(!clk_hw_data))
>>>> + goto err;
> [...]
>>>> +
>>>> +err:
>>>> + iounmap(loongson2_pll_base);
>>>> +}
>>>> +
>>>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
>>> Any reason this can't be a platform driver?
Your question is that why I don't use the platform_driver_register to
register clk and use CLK_OF_DECLARE ?
I was consider other clock controllers of Loongson-2 series may be
different with 2k1000 and I can add a line
CLK_OF_DECLARE() for compatible other platform in the future. eg.
CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
+ CLK_OF_DECLARE(xxx1, xxx2, xxx3); // for other clock controllers of
Loongson-2 series
>> For the compatible consideration of other clock controllers of
>> Loongson-2 series in the future, the way of using dts can be
>>
>> better compatible.
>>
> Sorry that sentence doesn't make sense to me. The use of dts doesn't
> require the use of CLK_OF_DECLARE.
yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can
use platform_driver_register
but my drvier not use platform_driver_register to register clk and use
CLK_OF_DECLARE to match of_clk_init.
Quoting zhuyinbo (2023-02-19 21:44:51)
>
> 在 2023/2/18 上午6:15, Stephen Boyd 写道:
> > Quoting zhuyinbo (2023-02-14 23:35:22)
> >> 在 2023/2/11 上午7:42, Stephen Boyd 写道:
> >>>> +
> >>>> +err:
> >>>> + iounmap(loongson2_pll_base);
> >>>> +}
> >>>> +
> >>>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
> >>> Any reason this can't be a platform driver?
>
> Your question is that why I don't use the platform_driver_register to
> register clk and use CLK_OF_DECLARE ?
Yes.
>
> I was consider other clock controllers of Loongson-2 series may be
> different with 2k1000 and I can add a line
>
> CLK_OF_DECLARE() for compatible other platform in the future. eg.
>
> CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
>
> + CLK_OF_DECLARE(xxx1, xxx2, xxx3); // for other clock controllers of
> Loongson-2 series
>
> >> For the compatible consideration of other clock controllers of
> >> Loongson-2 series in the future, the way of using dts can be
> >>
> >> better compatible.
> >>
> > Sorry that sentence doesn't make sense to me. The use of dts doesn't
> > require the use of CLK_OF_DECLARE.
>
> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can
> use platform_driver_register
>
> but my drvier not use platform_driver_register to register clk and use
> CLK_OF_DECLARE to match of_clk_init.
of_clk_init() is there to register clks that are needed for early init,
i.e. the clockevent/clocksource or the root interrupt controller
(irqchip). Otherwise, it isn't necessary to register clks via
of_clk_init().
在 2023/2/22 上午7:07, Stephen Boyd 写道:
> Quoting zhuyinbo (2023-02-19 21:44:51)
>> 在 2023/2/18 上午6:15, Stephen Boyd 写道:
>>> Quoting zhuyinbo (2023-02-14 23:35:22)
>>>> 在 2023/2/11 上午7:42, Stephen Boyd 写道:
>>>>>> +
>>>>>> +err:
>>>>>> + iounmap(loongson2_pll_base);
>>>>>> +}
>>>>>> +
>>>>>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
>>>>> Any reason this can't be a platform driver?
>> Your question is that why I don't use the platform_driver_register to
>> register clk and use CLK_OF_DECLARE ?
> Yes.
>
>> I was consider other clock controllers of Loongson-2 series may be
>> different with 2k1000 and I can add a line
>>
>> CLK_OF_DECLARE() for compatible other platform in the future. eg.
>>
>> CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init);
>>
>> + CLK_OF_DECLARE(xxx1, xxx2, xxx3); // for other clock controllers of
>> Loongson-2 series
>>
>>>> For the compatible consideration of other clock controllers of
>>>> Loongson-2 series in the future, the way of using dts can be
>>>>
>>>> better compatible.
>>>>
>>> Sorry that sentence doesn't make sense to me. The use of dts doesn't
>>> require the use of CLK_OF_DECLARE.
>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can
>> use platform_driver_register
>>
>> but my drvier not use platform_driver_register to register clk and use
>> CLK_OF_DECLARE to match of_clk_init.
> of_clk_init() is there to register clks that are needed for early init,
> i.e. the clockevent/clocksource or the root interrupt controller
> (irqchip). Otherwise, it isn't necessary to register clks via
> of_clk_init().
okay, I got it.
在 2023/2/22 上午10:02, zhuyinbo 写道:
>
> 在 2023/2/22 上午7:07, Stephen Boyd 写道:
>> Quoting zhuyinbo (2023-02-19 21:44:51)
>>> 在 2023/2/18 上午6:15, Stephen Boyd 写道:
>>>> Quoting zhuyinbo (2023-02-14 23:35:22)
>>>>> 在 2023/2/11 上午7:42, Stephen Boyd 写道:
>>>>>>> +
>>>>>>> +err:
>>>>>>> + iounmap(loongson2_pll_base);
>>>>>>> +}
>>>>>>> +
>>>>>>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk",
>>>>>>> loongson2_clocks_init);
>>>>>> Any reason this can't be a platform driver?
>>> Your question is that why I don't use the platform_driver_register to
>>> register clk and use CLK_OF_DECLARE ?
>> Yes.
>>
>>> I was consider other clock controllers of Loongson-2 series may be
>>> different with 2k1000 and I can add a line
>>>
>>> CLK_OF_DECLARE() for compatible other platform in the future. eg.
>>>
>>> CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk",
>>> loongson2_clocks_init);
>>>
>>> + CLK_OF_DECLARE(xxx1, xxx2, xxx3); // for other clock
>>> controllers of
>>> Loongson-2 series
>>>
>>>>> For the compatible consideration of other clock controllers of
>>>>> Loongson-2 series in the future, the way of using dts can be
>>>>>
>>>>> better compatible.
>>>>>
>>>> Sorry that sentence doesn't make sense to me. The use of dts doesn't
>>>> require the use of CLK_OF_DECLARE.
>>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can
>>> use platform_driver_register
>>>
>>> but my drvier not use platform_driver_register to register clk and use
>>> CLK_OF_DECLARE to match of_clk_init.
>> of_clk_init() is there to register clks that are needed for early init,
>> i.e. the clockevent/clocksource or the root interrupt controller
>> (irqchip). Otherwise, it isn't necessary to register clks via
>> of_clk_init().
> okay, I got it.
and, the time driver get clock by CCF that ask loongson2 clock driver
use CLK_OF_DECLARE
to match of_clk_init. because the timer_probe is very early and the
timer driver was use TIMER_OF_DECLARE
to match time_probe.
Quoting zhuyinbo (2023-02-21 21:10:14)
>
> >
> >>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can
> >>> use platform_driver_register
> >>>
> >>> but my drvier not use platform_driver_register to register clk and use
> >>> CLK_OF_DECLARE to match of_clk_init.
> >> of_clk_init() is there to register clks that are needed for early init,
> >> i.e. the clockevent/clocksource or the root interrupt controller
> >> (irqchip). Otherwise, it isn't necessary to register clks via
> >> of_clk_init().
> > okay, I got it.
>
> and, the time driver get clock by CCF that ask loongson2 clock driver
> use CLK_OF_DECLARE
>
> to match of_clk_init. because the timer_probe is very early and the
> timer driver was use TIMER_OF_DECLARE
>
> to match time_probe.
>
If you have a time driver that gets clks, register those early with
CLK_OF_DECLARE_DRIVER() and then have a platform driver for the rest of
the clk tree that registers clks later.
在 2023/2/23 上午5:00, Stephen Boyd 写道:
> Quoting zhuyinbo (2023-02-21 21:10:14)
>>>>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can
>>>>> use platform_driver_register
>>>>>
>>>>> but my drvier not use platform_driver_register to register clk and use
>>>>> CLK_OF_DECLARE to match of_clk_init.
>>>> of_clk_init() is there to register clks that are needed for early init,
>>>> i.e. the clockevent/clocksource or the root interrupt controller
>>>> (irqchip). Otherwise, it isn't necessary to register clks via
>>>> of_clk_init().
>>> okay, I got it.
>> and, the time driver get clock by CCF that ask loongson2 clock driver
>> use CLK_OF_DECLARE
>>
>> to match of_clk_init. because the timer_probe is very early and the
>> timer driver was use TIMER_OF_DECLARE
>>
>> to match time_probe.
>>
> If you have a time driver that gets clks, register those early with
> CLK_OF_DECLARE_DRIVER() and then have a platform driver for the rest of
> the clk tree that registers clks later.
okay, I got it. and this series patch I will use platform driver.
later, if the loongson2 time driver use the
CLK_OF_DECLARE style I will make a alone time clk driver for it that
use CLK_OF_DECLARE_DRIVER()
Quoting zhuyinbo (2023-02-22 18:16:49)
>
> 在 2023/2/23 上午5:00, Stephen Boyd 写道:
> > Quoting zhuyinbo (2023-02-21 21:10:14)
> >>>>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can
> >>>>> use platform_driver_register
> >>>>>
> >>>>> but my drvier not use platform_driver_register to register clk and use
> >>>>> CLK_OF_DECLARE to match of_clk_init.
> >>>> of_clk_init() is there to register clks that are needed for early init,
> >>>> i.e. the clockevent/clocksource or the root interrupt controller
> >>>> (irqchip). Otherwise, it isn't necessary to register clks via
> >>>> of_clk_init().
> >>> okay, I got it.
> >> and, the time driver get clock by CCF that ask loongson2 clock driver
> >> use CLK_OF_DECLARE
> >>
> >> to match of_clk_init. because the timer_probe is very early and the
> >> timer driver was use TIMER_OF_DECLARE
> >>
> >> to match time_probe.
> >>
> > If you have a time driver that gets clks, register those early with
> > CLK_OF_DECLARE_DRIVER() and then have a platform driver for the rest of
> > the clk tree that registers clks later.
>
> okay, I got it. and this series patch I will use platform driver.
> later, if the loongson2 time driver use the
>
> CLK_OF_DECLARE style I will make a alone time clk driver for it that
> use CLK_OF_DECLARE_DRIVER()
>
It can be the same file if you want. But then it can't be a module. Up
to you what you want to do there.
在 2023/2/25 上午3:10, Stephen Boyd 写道:
> Quoting zhuyinbo (2023-02-22 18:16:49)
>> 在 2023/2/23 上午5:00, Stephen Boyd 写道:
>>> Quoting zhuyinbo (2023-02-21 21:10:14)
>>>>>>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can
>>>>>>> use platform_driver_register
>>>>>>>
>>>>>>> but my drvier not use platform_driver_register to register clk and use
>>>>>>> CLK_OF_DECLARE to match of_clk_init.
>>>>>> of_clk_init() is there to register clks that are needed for early init,
>>>>>> i.e. the clockevent/clocksource or the root interrupt controller
>>>>>> (irqchip). Otherwise, it isn't necessary to register clks via
>>>>>> of_clk_init().
>>>>> okay, I got it.
>>>> and, the time driver get clock by CCF that ask loongson2 clock driver
>>>> use CLK_OF_DECLARE
>>>>
>>>> to match of_clk_init. because the timer_probe is very early and the
>>>> timer driver was use TIMER_OF_DECLARE
>>>>
>>>> to match time_probe.
>>>>
>>> If you have a time driver that gets clks, register those early with
>>> CLK_OF_DECLARE_DRIVER() and then have a platform driver for the rest of
>>> the clk tree that registers clks later.
>> okay, I got it. and this series patch I will use platform driver.
>> later, if the loongson2 time driver use the
>>
>> CLK_OF_DECLARE style I will make a alone time clk driver for it that
>> use CLK_OF_DECLARE_DRIVER()
>>
> It can be the same file if you want. But then it can't be a module. Up
> to you what you want to do there.
okay, I got it.