Message ID | 20221129034157.15036-2-zhuyinbo@loongson.cn |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:f944:0:0:0:0:0 with SMTP id q4csp113238wrr; Mon, 28 Nov 2022 19:58:30 -0800 (PST) X-Google-Smtp-Source: AA0mqf4WxYQw+XfgEC2poxx5Rh/rZUL7ff6Ak/Gqkx5Dxc4O8gg82Bv6/Aq+uytObqdPy1CFpWsm X-Received: by 2002:a17:906:94e:b0:7ba:4617:3f17 with SMTP id j14-20020a170906094e00b007ba46173f17mr24213488ejd.226.1669694310510; Mon, 28 Nov 2022 19:58:30 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1669694310; cv=none; d=google.com; s=arc-20160816; b=GMFK8J95CgVOkTsuokylXul+vKtSXbhS9BSMlg/Mk03tc6HvnJkUKfEcFf05uSW9T0 jTNTEhbgg9gDbAPNGgLpSaBNmfSoDZO0Dg/7NXZA8uJ2z/mvJt1lMopLXfGZAz39shd5 9BGtzq7IECdFkWDnK3mLG6cuBYrjylGi6j4EIMkqA50RCZypag7pON1s+dQe0hhPo2G0 iV+inAmDxHhdyS6JvqPGsFUkZRA6cDoG9hPt71LcET/QzGGb04kV34SNQVCG4C4BN/4I PrpCtsjfwpSDbdKnjeRUvHNS8q2M98uq9hNhvRD9sxmS3MATqS09AD24q9xfaPEAKsyp mn8Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:to:from; bh=xo8uC71SXA4CO3Qzezjvb3HHzu0A8oJkXrtZlRMkZ3g=; b=CUTb12lzaPIFi+PURBZS+MY3F00oMl/iGkckLuVzNRWzuCUg95PG0W28AGcQHlI6U8 8kgEkDwanUeaitsX3N5CMTHOT8IMFpuyKsoegllihMiLQx+j16Kc7UEwmILgQuPtvocy 0H4FH4Ka7PPS5lPZ/9R/PYlLHlVP8IyFSoYZnxx226xKJNVwhrVdR8t8KkyK5o6bYnZL schHgw7Sl5RRYQmQ8eNUcg/PcHHkLcdiW8hxbaq6SLC2VhBbm5ARCzHEaJ0HO62StuOd YkGdqG4uiq3I05nzWAKiHxzm5+S9O923RRFaFI9+0ht1UbQ43dItmwTYETSgS+uRNWbG 58MA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id sh36-20020a1709076ea400b0079bf804c0c4si11392928ejc.103.2022.11.28.19.57.54; Mon, 28 Nov 2022 19:58:30 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235509AbiK2DmS (ORCPT <rfc822;gah0developer@gmail.com> + 99 others); Mon, 28 Nov 2022 22:42:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59214 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235546AbiK2DmI (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Mon, 28 Nov 2022 22:42:08 -0500 Received: from loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 7C5CF4B773; Mon, 28 Nov 2022 19:42:06 -0800 (PST) Received: from loongson.cn (unknown [10.180.13.64]) by gateway (Coremail) with SMTP id _____8Dx+OiMf4Vjb+gBAA--.1336S3; Tue, 29 Nov 2022 11:42:04 +0800 (CST) Received: from localhost.localdomain (unknown [10.180.13.64]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxPuCGf4VjjecdAA--.10584S3; Tue, 29 Nov 2022 11:42:02 +0800 (CST) From: Yinbo Zhu <zhuyinbo@loongson.cn> To: Michael Turquette <mturquette@baylibre.com>, Stephen Boyd <sboyd@kernel.org>, Rob Herring <robh+dt@kernel.org>, Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>, Huacai Chen <chenhuacai@kernel.org>, WANG Xuerui <kernel@xen0n.name>, Jiaxun Yang <jiaxun.yang@flygoat.com>, Jianmin Lv <lvjianmin@loongson.cn>, Yang Li <yang.lee@linux.alibaba.com>, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, loongarch@lists.linux.dev, Yinbo Zhu <zhuyinbo@loongson.cn> Subject: [PATCH v10 2/4] clk: clk-loongson2: add clock controller driver support Date: Tue, 29 Nov 2022 11:41:55 +0800 Message-Id: <20221129034157.15036-2-zhuyinbo@loongson.cn> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20221129034157.15036-1-zhuyinbo@loongson.cn> References: <20221129034157.15036-1-zhuyinbo@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CM-TRANSID: AQAAf8AxPuCGf4VjjecdAA--.10584S3 X-CM-SenderInfo: 52kx5xhqerqz5rrqw2lrqou0/ X-Coremail-Antispam: 1Uk129KBjvJXoW3trWkXFyUWF13XFy3ur1xKrg_yoWkWw1DpF WfAay5WrWjqr45uFsxtryDGr15Aas3Ca47AF43Ga4jkrZ7X345Wr40yFy8AF4UZrWkAay2 vFZagrW8CFs8WwUanT9S1TB71UUUUjDqnTZGkaVYY2UrUUUUj1kv1TuYvTs0mT0YCTnIWj qI5I8CrVACY4xI64kE6c02F40Ex7xfYxn0WfASr-VFAUDa7-sFnT9fnUUIcSsGvfJTRUUU bS8Fc2x0x2IEx4CE42xK8VAvwI8IcIk0rVWrJVCq3wAFIxvE14AKwVWUXVWUAwA2ocxC64 kIII0Yj41l84x0c7CEw4AK67xGY2AK021l84ACjcxK6xIIjxv20xvE14v26ryj6F1UM28E F7xvwVC0I7IYx2IY6xkF7I0E14v26r4j6F4UM28EF7xvwVC2z280aVAFwI0_Gr1j6F4UJw A2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_Cr1j6rxdM2kKe7AKxVWUAVWUtwAS0I0E0xvYzxvE 52x082IY62kv0487Mc804VCY07AIYIkI8VC2zVCFFI0UMc02F40EFcxC0VAKzVAqx4xG6I 80ewAv7VC0I7IYx2IY67AKxVWUtVWrXwAv7VC2z280aVAFwI0_Cr0_Gr1UMcvjeVCFs4IE 7xkEbVWUJVW8JwACjcxG0xvY0x0EwIxGrwCY1x0262kKe7AKxVWUAVWUtwCF04k20xvY0x 0EwIxGrwCF04k20xvE74AGY7Cv6cx26rWl4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1l4IxYO2xF xVAFwI0_JF0_Jw1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x8GjcxK67AKxVWUGVWUWw C2zVAF1VAY17CE14v26r1q6r43MIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8IcVAFwI0_Xr0_ Ar1lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4j6F4UMIIF0xvE42xK8VAvwI8IcIk0rVWUJV WUCwCI42IY6I8E87Iv67AKxVWxJVW8Jr1lIxAIcVC2z280aVCY1x0267AKxVW8Jr0_Cr1U YxBIdaVFxhVjvjDU0xZFpf9x07UR89_UUUUU= X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,SPF_HELO_PASS, SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1750801381383973204?= X-GMAIL-MSGID: =?utf-8?q?1750801381383973204?= |
Series |
[v10,1/4] dt-bindings: clock: add loongson-2 clock include file
|
|
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
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);
在 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 >
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);
在 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);
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);
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);
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?
在 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.
Quoting zhuyinbo (2023-02-14 23:35:22) > > 在 2023/2/11 上午7:42, Stephen Boyd 写道: > > Quoting Yinbo Zhu (2022-11-28 19:41:55) > > > >> + > >> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) & > >> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH); > >> + > >> + rate = parent_rate * (mult + 1); > >> + do_div(rate, 8); > > Why is do_div() being used? > no expecial reason, I only want to get a result that rate divide 8. Ok, you can use div_u64() here and simplify. > > > > you meaning is to use clk_parent_data to rework > loongson2_clk_pll_register and drop > > loongson2_obtain_fixed_clk_hw ? Yes > > > > >> +} > >> + > >> +static void __init loongson2_clocks_init(struct device_node *np) > >> +{ > >> + struct clk_hw **hws; > >> + struct clk_hw_onecell_data *clk_hw_data; > >> + spinlock_t loongson2_clk_lock; > >> + > >> + loongson2_pll_base = of_iomap(np, 0); > >> + > >> + if (!loongson2_pll_base) { > >> + pr_err("clk: unable to map loongson2 clk registers\n"); > >> + goto err; > > return? > sorry, I don't get it. you meaning is that remove "goto err". Instead, > add a "return". Yes. > > > >> + } > >> + > >> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END), > >> + GFP_KERNEL); > >> + if (WARN_ON(!clk_hw_data)) > >> + goto err; [...] > >> + > >> +err: > >> + iounmap(loongson2_pll_base); > >> +} > >> + > >> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init); > > Any reason this can't be a platform driver? > > For the compatible consideration of other clock controllers of > Loongson-2 series in the future, the way of using dts can be > > better compatible. > Sorry that sentence doesn't make sense to me. The use of dts doesn't require the use of CLK_OF_DECLARE.
在 2023/2/18 上午6:15, Stephen Boyd 写道: > Quoting zhuyinbo (2023-02-14 23:35:22) >> 在 2023/2/11 上午7:42, Stephen Boyd 写道: >>> Quoting Yinbo Zhu (2022-11-28 19:41:55) >>> >>>> + >>>> + mult = (val >> LOONGSON2_USB_FREQSCALE_SHIFT) & >>>> + clk_div_mask(LOONGSON2_USB_FREQSCALE_WIDTH); >>>> + >>>> + rate = parent_rate * (mult + 1); >>>> + do_div(rate, 8); >>> Why is do_div() being used? >> no expecial reason, I only want to get a result that rate divide 8. > Ok, you can use div_u64() here and simplify. okay, I got it. > >> you meaning is to use clk_parent_data to rework >> loongson2_clk_pll_register and drop >> >> loongson2_obtain_fixed_clk_hw ? > Yes > >>>> +} >>>> + >>>> +static void __init loongson2_clocks_init(struct device_node *np) >>>> +{ >>>> + struct clk_hw **hws; >>>> + struct clk_hw_onecell_data *clk_hw_data; >>>> + spinlock_t loongson2_clk_lock; >>>> + >>>> + loongson2_pll_base = of_iomap(np, 0); >>>> + >>>> + if (!loongson2_pll_base) { >>>> + pr_err("clk: unable to map loongson2 clk registers\n"); >>>> + goto err; >>> return? >> sorry, I don't get it. you meaning is that remove "goto err". Instead, >> add a "return". > Yes. > >>>> + } >>>> + >>>> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, LOONGSON2_CLK_END), >>>> + GFP_KERNEL); >>>> + if (WARN_ON(!clk_hw_data)) >>>> + goto err; > [...] >>>> + >>>> +err: >>>> + iounmap(loongson2_pll_base); >>>> +} >>>> + >>>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init); >>> Any reason this can't be a platform driver? Your question is that why I don't use the platform_driver_register to register clk and use CLK_OF_DECLARE ? I was consider other clock controllers of Loongson-2 series may be different with 2k1000 and I can add a line CLK_OF_DECLARE() for compatible other platform in the future. eg. CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init); + CLK_OF_DECLARE(xxx1, xxx2, xxx3); // for other clock controllers of Loongson-2 series >> For the compatible consideration of other clock controllers of >> Loongson-2 series in the future, the way of using dts can be >> >> better compatible. >> > Sorry that sentence doesn't make sense to me. The use of dts doesn't > require the use of CLK_OF_DECLARE. yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can use platform_driver_register but my drvier not use platform_driver_register to register clk and use CLK_OF_DECLARE to match of_clk_init.
Quoting zhuyinbo (2023-02-19 21:44:51) > > 在 2023/2/18 上午6:15, Stephen Boyd 写道: > > Quoting zhuyinbo (2023-02-14 23:35:22) > >> 在 2023/2/11 上午7:42, Stephen Boyd 写道: > >>>> + > >>>> +err: > >>>> + iounmap(loongson2_pll_base); > >>>> +} > >>>> + > >>>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init); > >>> Any reason this can't be a platform driver? > > Your question is that why I don't use the platform_driver_register to > register clk and use CLK_OF_DECLARE ? Yes. > > I was consider other clock controllers of Loongson-2 series may be > different with 2k1000 and I can add a line > > CLK_OF_DECLARE() for compatible other platform in the future. eg. > > CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init); > > + CLK_OF_DECLARE(xxx1, xxx2, xxx3); // for other clock controllers of > Loongson-2 series > > >> For the compatible consideration of other clock controllers of > >> Loongson-2 series in the future, the way of using dts can be > >> > >> better compatible. > >> > > Sorry that sentence doesn't make sense to me. The use of dts doesn't > > require the use of CLK_OF_DECLARE. > > yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can > use platform_driver_register > > but my drvier not use platform_driver_register to register clk and use > CLK_OF_DECLARE to match of_clk_init. of_clk_init() is there to register clks that are needed for early init, i.e. the clockevent/clocksource or the root interrupt controller (irqchip). Otherwise, it isn't necessary to register clks via of_clk_init().
在 2023/2/22 上午7:07, Stephen Boyd 写道: > Quoting zhuyinbo (2023-02-19 21:44:51) >> 在 2023/2/18 上午6:15, Stephen Boyd 写道: >>> Quoting zhuyinbo (2023-02-14 23:35:22) >>>> 在 2023/2/11 上午7:42, Stephen Boyd 写道: >>>>>> + >>>>>> +err: >>>>>> + iounmap(loongson2_pll_base); >>>>>> +} >>>>>> + >>>>>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init); >>>>> Any reason this can't be a platform driver? >> Your question is that why I don't use the platform_driver_register to >> register clk and use CLK_OF_DECLARE ? > Yes. > >> I was consider other clock controllers of Loongson-2 series may be >> different with 2k1000 and I can add a line >> >> CLK_OF_DECLARE() for compatible other platform in the future. eg. >> >> CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", loongson2_clocks_init); >> >> + CLK_OF_DECLARE(xxx1, xxx2, xxx3); // for other clock controllers of >> Loongson-2 series >> >>>> For the compatible consideration of other clock controllers of >>>> Loongson-2 series in the future, the way of using dts can be >>>> >>>> better compatible. >>>> >>> Sorry that sentence doesn't make sense to me. The use of dts doesn't >>> require the use of CLK_OF_DECLARE. >> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can >> use platform_driver_register >> >> but my drvier not use platform_driver_register to register clk and use >> CLK_OF_DECLARE to match of_clk_init. > of_clk_init() is there to register clks that are needed for early init, > i.e. the clockevent/clocksource or the root interrupt controller > (irqchip). Otherwise, it isn't necessary to register clks via > of_clk_init(). okay, I got it.
在 2023/2/22 上午10:02, zhuyinbo 写道: > > 在 2023/2/22 上午7:07, Stephen Boyd 写道: >> Quoting zhuyinbo (2023-02-19 21:44:51) >>> 在 2023/2/18 上午6:15, Stephen Boyd 写道: >>>> Quoting zhuyinbo (2023-02-14 23:35:22) >>>>> 在 2023/2/11 上午7:42, Stephen Boyd 写道: >>>>>>> + >>>>>>> +err: >>>>>>> + iounmap(loongson2_pll_base); >>>>>>> +} >>>>>>> + >>>>>>> +CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", >>>>>>> loongson2_clocks_init); >>>>>> Any reason this can't be a platform driver? >>> Your question is that why I don't use the platform_driver_register to >>> register clk and use CLK_OF_DECLARE ? >> Yes. >> >>> I was consider other clock controllers of Loongson-2 series may be >>> different with 2k1000 and I can add a line >>> >>> CLK_OF_DECLARE() for compatible other platform in the future. eg. >>> >>> CLK_OF_DECLARE(loongson2_clk, "loongson,ls2k-clk", >>> loongson2_clocks_init); >>> >>> + CLK_OF_DECLARE(xxx1, xxx2, xxx3); // for other clock >>> controllers of >>> Loongson-2 series >>> >>>>> For the compatible consideration of other clock controllers of >>>>> Loongson-2 series in the future, the way of using dts can be >>>>> >>>>> better compatible. >>>>> >>>> Sorry that sentence doesn't make sense to me. The use of dts doesn't >>>> require the use of CLK_OF_DECLARE. >>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can >>> use platform_driver_register >>> >>> but my drvier not use platform_driver_register to register clk and use >>> CLK_OF_DECLARE to match of_clk_init. >> of_clk_init() is there to register clks that are needed for early init, >> i.e. the clockevent/clocksource or the root interrupt controller >> (irqchip). Otherwise, it isn't necessary to register clks via >> of_clk_init(). > okay, I got it. and, the time driver get clock by CCF that ask loongson2 clock driver use CLK_OF_DECLARE to match of_clk_init. because the timer_probe is very early and the timer driver was use TIMER_OF_DECLARE to match time_probe.
Quoting zhuyinbo (2023-02-21 21:10:14) > > > > >>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can > >>> use platform_driver_register > >>> > >>> but my drvier not use platform_driver_register to register clk and use > >>> CLK_OF_DECLARE to match of_clk_init. > >> of_clk_init() is there to register clks that are needed for early init, > >> i.e. the clockevent/clocksource or the root interrupt controller > >> (irqchip). Otherwise, it isn't necessary to register clks via > >> of_clk_init(). > > okay, I got it. > > and, the time driver get clock by CCF that ask loongson2 clock driver > use CLK_OF_DECLARE > > to match of_clk_init. because the timer_probe is very early and the > timer driver was use TIMER_OF_DECLARE > > to match time_probe. > If you have a time driver that gets clks, register those early with CLK_OF_DECLARE_DRIVER() and then have a platform driver for the rest of the clk tree that registers clks later.
在 2023/2/23 上午5:00, Stephen Boyd 写道: > Quoting zhuyinbo (2023-02-21 21:10:14) >>>>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can >>>>> use platform_driver_register >>>>> >>>>> but my drvier not use platform_driver_register to register clk and use >>>>> CLK_OF_DECLARE to match of_clk_init. >>>> of_clk_init() is there to register clks that are needed for early init, >>>> i.e. the clockevent/clocksource or the root interrupt controller >>>> (irqchip). Otherwise, it isn't necessary to register clks via >>>> of_clk_init(). >>> okay, I got it. >> and, the time driver get clock by CCF that ask loongson2 clock driver >> use CLK_OF_DECLARE >> >> to match of_clk_init. because the timer_probe is very early and the >> timer driver was use TIMER_OF_DECLARE >> >> to match time_probe. >> > If you have a time driver that gets clks, register those early with > CLK_OF_DECLARE_DRIVER() and then have a platform driver for the rest of > the clk tree that registers clks later. okay, I got it. and this series patch I will use platform driver. later, if the loongson2 time driver use the CLK_OF_DECLARE style I will make a alone time clk driver for it that use CLK_OF_DECLARE_DRIVER()
Quoting zhuyinbo (2023-02-22 18:16:49) > > 在 2023/2/23 上午5:00, Stephen Boyd 写道: > > Quoting zhuyinbo (2023-02-21 21:10:14) > >>>>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can > >>>>> use platform_driver_register > >>>>> > >>>>> but my drvier not use platform_driver_register to register clk and use > >>>>> CLK_OF_DECLARE to match of_clk_init. > >>>> of_clk_init() is there to register clks that are needed for early init, > >>>> i.e. the clockevent/clocksource or the root interrupt controller > >>>> (irqchip). Otherwise, it isn't necessary to register clks via > >>>> of_clk_init(). > >>> okay, I got it. > >> and, the time driver get clock by CCF that ask loongson2 clock driver > >> use CLK_OF_DECLARE > >> > >> to match of_clk_init. because the timer_probe is very early and the > >> timer driver was use TIMER_OF_DECLARE > >> > >> to match time_probe. > >> > > If you have a time driver that gets clks, register those early with > > CLK_OF_DECLARE_DRIVER() and then have a platform driver for the rest of > > the clk tree that registers clks later. > > okay, I got it. and this series patch I will use platform driver. > later, if the loongson2 time driver use the > > CLK_OF_DECLARE style I will make a alone time clk driver for it that > use CLK_OF_DECLARE_DRIVER() > It can be the same file if you want. But then it can't be a module. Up to you what you want to do there.
在 2023/2/25 上午3:10, Stephen Boyd 写道: > Quoting zhuyinbo (2023-02-22 18:16:49) >> 在 2023/2/23 上午5:00, Stephen Boyd 写道: >>> Quoting zhuyinbo (2023-02-21 21:10:14) >>>>>>> yes, the use of dts doesn't require the use of CLK_OF_DECLARE and can >>>>>>> use platform_driver_register >>>>>>> >>>>>>> but my drvier not use platform_driver_register to register clk and use >>>>>>> CLK_OF_DECLARE to match of_clk_init. >>>>>> of_clk_init() is there to register clks that are needed for early init, >>>>>> i.e. the clockevent/clocksource or the root interrupt controller >>>>>> (irqchip). Otherwise, it isn't necessary to register clks via >>>>>> of_clk_init(). >>>>> okay, I got it. >>>> and, the time driver get clock by CCF that ask loongson2 clock driver >>>> use CLK_OF_DECLARE >>>> >>>> to match of_clk_init. because the timer_probe is very early and the >>>> timer driver was use TIMER_OF_DECLARE >>>> >>>> to match time_probe. >>>> >>> If you have a time driver that gets clks, register those early with >>> CLK_OF_DECLARE_DRIVER() and then have a platform driver for the rest of >>> the clk tree that registers clks later. >> okay, I got it. and this series patch I will use platform driver. >> later, if the loongson2 time driver use the >> >> CLK_OF_DECLARE style I will make a alone time clk driver for it that >> use CLK_OF_DECLARE_DRIVER() >> > It can be the same file if you want. But then it can't be a module. Up > to you what you want to do there. okay, I got it.
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);