diff mbox series

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

Message ID 20221129034157.15036-2-zhuyinbo@loongson.cn (mailing list archive)
State Changes Requested, archived
Headers show
Series [v10,1/4] dt-bindings: clock: add loongson-2 clock include file | expand

Commit Message

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

Signed-off-by: Yinbo Zhu <zhuyinbo@loongson.cn>
---
Change in 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

Comments

Xiaochuan Mao Nov. 30, 2022, 3:32 a.m. UTC | #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 <zhuyinbo@loongson.cn>
> ---
> 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 <zhuyinbo@loongson.cn>
>   L:	linux-clk@vger.kernel.org
>   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 <zhuyinbo@loongson.cn>
> + * 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);
Yinbo Zhu Nov. 30, 2022, 10:16 a.m. UTC | #2
在 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<zhuyinbo@loongson.cn>
>> ---
>> 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<zhuyinbo@loongson.cn>
>>   L:	linux-clk@vger.kernel.org
>>   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<zhuyinbo@loongson.cn>
>> + * 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
>
Xiaochuan Mao Nov. 30, 2022, 1:14 p.m. UTC | #3
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 <zhuyinbo@loongson.cn>
> ---
> 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 <zhuyinbo@loongson.cn>
>   L:	linux-clk@vger.kernel.org
>   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 <zhuyinbo@loongson.cn>
> + * 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);
Yinbo Zhu Nov. 30, 2022, 10:49 p.m. UTC | #4
在 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 <zhuyinbo@loongson.cn>
>> ---
>> 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 <zhuyinbo@loongson.cn>
>>   L:    linux-clk@vger.kernel.org
>>   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 <zhuyinbo@loongson.cn>
>> + * 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);
Yinbo Zhu Dec. 7, 2022, 2:15 a.m. UTC | #5
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 <zhuyinbo@loongson.cn>
> ---
> 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 <zhuyinbo@loongson.cn>
>   L:	linux-clk@vger.kernel.org
>   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 <zhuyinbo@loongson.cn>
> + * 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);
Yinbo Zhu Feb. 1, 2023, 4:37 a.m. UTC | #6
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 <zhuyinbo@loongson.cn>
> ---
> 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 <zhuyinbo@loongson.cn>
>   L:	linux-clk@vger.kernel.org
>   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 <zhuyinbo@loongson.cn>
> + * 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);
Stephen Boyd Feb. 10, 2023, 11:42 p.m. UTC | #7
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 <zhuyinbo@loongson.cn>
> + * 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?
Yinbo Zhu Feb. 15, 2023, 7:35 a.m. UTC | #8
在 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 <zhuyinbo@loongson.cn>
>> + * 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.
Stephen Boyd Feb. 17, 2023, 10:15 p.m. UTC | #9
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.
Yinbo Zhu Feb. 20, 2023, 5:44 a.m. UTC | #10
在 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.
Stephen Boyd Feb. 21, 2023, 11:07 p.m. UTC | #11
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().
Yinbo Zhu Feb. 22, 2023, 2:02 a.m. UTC | #12
在 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.
Yinbo Zhu Feb. 22, 2023, 5:10 a.m. UTC | #13
在 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.
Stephen Boyd Feb. 22, 2023, 9 p.m. UTC | #14
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.
Yinbo Zhu Feb. 23, 2023, 2:16 a.m. UTC | #15
在 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()
Stephen Boyd Feb. 24, 2023, 7:10 p.m. UTC | #16
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.
Yinbo Zhu Feb. 27, 2023, 1:54 a.m. UTC | #17
在 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.
diff mbox series

Patch

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 <zhuyinbo@loongson.cn>
 L:	linux-clk@vger.kernel.org
 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 <zhuyinbo@loongson.cn>
+ * 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);