[PATCH RFC 2/2] clk: add clock framework for HiSilicon SoCs

Yang Xiwen via B4 Relay devnull+forbidden405.outlook.com at kernel.org
Fri Jan 19 14:36:19 CET 2024


From: Yang Xiwen <forbidden405 at outlook.com>

Hi3798 Series SoCs have a CRG (Clock Reset Generator) module which
manages all clocks and resets of the SoC.

The first supported chip is Hi3798MV200.  The unused clocks are not
registered to save space and time. Only necessary clocks are
implemented right now.

Signed-off-by: Yang Xiwen <forbidden405 at outlook.com>
---
 drivers/clk/Kconfig                     |   7 ++
 drivers/clk/Makefile                    |   1 +
 drivers/clk/hisilicon/Kconfig           |  14 +++
 drivers/clk/hisilicon/Makefile          |   8 ++
 drivers/clk/hisilicon/clk-hi3798mv200.c | 213 ++++++++++++++++++++++++++++++++
 drivers/clk/hisilicon/clk.c             | 102 +++++++++++++++
 drivers/clk/hisilicon/clk.h             |  55 +++++++++
 include/dt-bindings/clock/histb-clock.h |   4 +
 8 files changed, 404 insertions(+)

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 017dd260a5..4c5ac46b26 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -127,6 +127,12 @@ config CLK_ICS8N3QV01
 	  Crystal Oscillator). The output frequency can be programmed via an
 	  I2C interface.
 
+config CLK_HISI
+	bool "Enable Hisilicon Clock Framework"
+	depends on CLK && CLK_CCF
+	help
+	  Support for Hisilicon Clock Framework.
+
 config CLK_INTEL
 	bool "Enable clock driver for Intel x86"
 	depends on CLK && X86
@@ -249,6 +255,7 @@ config CLK_ZYNQMP
 source "drivers/clk/analogbits/Kconfig"
 source "drivers/clk/at91/Kconfig"
 source "drivers/clk/exynos/Kconfig"
+source "drivers/clk/hisilicon/Kconfig"
 source "drivers/clk/imx/Kconfig"
 source "drivers/clk/meson/Kconfig"
 source "drivers/clk/microchip/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 638ad04bae..90e7e1b5f4 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o
 obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
 obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o
 obj-$(CONFIG_CLK_EXYNOS) += exynos/
+obj-$(CONFIG_CLK_HISI) += hisilicon/
 obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
 obj-$(CONFIG_CLK_K210) += clk_k210.o
 obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig
new file mode 100644
index 0000000000..caa51b7831
--- /dev/null
+++ b/drivers/clk/hisilicon/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+if CLK_HISI
+menu "HiSilicon CRG Driver"
+
+config COMMON_CLK_HI3798MV200
+	tristate "Hi3798MV200 CRG Driver"
+	select RESET_HISILICON
+	depends on ARCH_HISTB
+	help
+	  Build the CRG driver for Hi3798MV200.
+
+endmenu
+endif
diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile
new file mode 100644
index 0000000000..85a0ffb4a1
--- /dev/null
+++ b/drivers/clk/hisilicon/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Hisilicon Clock specific Makefile
+#
+
+obj-y	+= clk.o
+
+obj-$(CONFIG_COMMON_CLK_HI3798MV200)	+= clk-hi3798mv200.o
diff --git a/drivers/clk/hisilicon/clk-hi3798mv200.c b/drivers/clk/hisilicon/clk-hi3798mv200.c
new file mode 100644
index 0000000000..01bb20d940
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hi3798mv200.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hi3798MV200 Clock and Reset Generator Driver.
+ * Adapted from clk-hi3798cv200.c.
+ *
+ * Copyright (c) 2024 Yang Xiwen <forbidden405 at outlook.com>
+ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd.
+ */
+
+#include <dt-bindings/clock/histb-clock.h>
+#include <linux/clk-provider.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dm/read.h>
+
+#include "clk.h"
+
+/* hi3798MV200 core CRG */
+#define HI3798MV200_INNER_CLK_OFFSET		64
+#define HI3798MV200_FIXED_12M			65
+#define HI3798MV200_FIXED_24M			66
+#define HI3798MV200_FIXED_25M			67
+#define HI3798MV200_FIXED_27M			68
+#define HI3798MV200_FIXED_48M			69
+#define HI3798MV200_FIXED_50M			70
+#define HI3798MV200_FIXED_54M			71
+#define HI3798MV200_FIXED_60M			72
+#define HI3798MV200_FIXED_75M			73
+#define HI3798MV200_FIXED_100M			74
+#define HI3798MV200_FIXED_150M			75
+#define HI3798MV200_FIXED_166P5M		76
+#define HI3798MV200_FIXED_200M			77
+#define HI3798MV200_FIXED_250M			78
+#define HI3798MV200_FIXED_300M			79
+#define HI3798MV200_FIXED_400M			80
+#define HI3798MV200_MMC_MUX			81
+#define HI3798MV200_COMBPHY1_MUX		82
+#define HI3798MV200_SDIO0_MUX			83
+#define HI3798MV200_COMBPHY0_MUX		84
+#define HI3798MV200_SDIO1_MUX			85
+#define HI3798MV200_ETH_MUX			86
+
+static const struct hisi_fixed_rate_clock hi3798mv200_fixed_rate_clks[] = {
+	{ HISTB_OSC_CLK, "clk_osc", 24000000, },
+	{ HISTB_APB_CLK, "clk_apb", 100000000, },
+	{ HISTB_AHB_CLK, "clk_ahb", 200000000, },
+	{ HI3798MV200_FIXED_12M, "12m", 12000000, },
+	{ HI3798MV200_FIXED_24M, "24m", 24000000, },
+	{ HI3798MV200_FIXED_25M, "25m", 25000000, },
+	{ HI3798MV200_FIXED_27M, "27m", 27000000, },
+	{ HI3798MV200_FIXED_48M, "48m", 48000000, },
+	{ HI3798MV200_FIXED_50M, "50m", 50000000, },
+	{ HI3798MV200_FIXED_54M, "54m", 54000000, },
+	{ HI3798MV200_FIXED_60M, "60m", 60000000, },
+	{ HI3798MV200_FIXED_75M, "75m", 75000000, },
+	{ HI3798MV200_FIXED_100M, "100m", 100000000, },
+	{ HI3798MV200_FIXED_150M, "150m", 150000000, },
+	{ HI3798MV200_FIXED_166P5M, "166p5m", 165000000, },
+	{ HI3798MV200_FIXED_200M, "200m", 200000000, },
+	{ HI3798MV200_FIXED_250M, "250m", 250000000, },
+};
+
+static const char *const mmc_mux_p[] = {
+		"100m", "50m", "25m", "200m", "150m" };
+static u32 mmc_mux_table[] = {0, 1, 2, 3, 6};
+
+static const char *const comphy_mux_p[] = {
+		"25m", "100m"};
+static u32 comphy_mux_table[] = {0, 1};
+
+static const char *const sdio_mux_p[] = {
+		"100m", "50m", "150m", "166p5m" };
+static u32 sdio_mux_table[] = {0, 1, 2, 3};
+
+static const char *const eth_mux_p[] = {
+		"54m", "27m" };
+static u32 eth_mux_table[] = {0, 1};
+
+static struct hisi_mux_clock hi3798mv200_mux_clks[] = {
+	{ HI3798MV200_MMC_MUX, "mmc_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p),
+		CLK_SET_RATE_PARENT, 0xa0, 8, 3, 0, mmc_mux_table, },
+	{ HI3798MV200_COMBPHY0_MUX, "combphy0_mux",
+		comphy_mux_p, ARRAY_SIZE(comphy_mux_p),
+		CLK_SET_RATE_PARENT, 0x188, 3, 1, 0, comphy_mux_table, },
+	{ HI3798MV200_SDIO0_MUX, "sdio0_mux", sdio_mux_p,
+		ARRAY_SIZE(sdio_mux_p), CLK_SET_RATE_PARENT,
+		0x9c, 8, 2, 0, sdio_mux_table, },
+	{ HI3798MV200_SDIO1_MUX, "sdio1_mux", sdio_mux_p,
+		ARRAY_SIZE(sdio_mux_p), CLK_SET_RATE_PARENT,
+		0x28c, 8, 2, 0, sdio_mux_table, },
+	{ HI3798MV200_ETH_MUX, "eth_mux", eth_mux_p,
+		ARRAY_SIZE(eth_mux_p), CLK_SET_RATE_PARENT,
+		0xd0, 2, 1, 0, eth_mux_table, },
+};
+
+static const struct hisi_gate_clock hi3798mv200_gate_clks[] = {
+	/* SDIO0 */
+	{ HISTB_SDIO0_BIU_CLK, "clk_sdio0_biu", "200m",
+			CLK_SET_RATE_PARENT, 0x9c, 0, 0, },
+	{ HISTB_SDIO0_CIU_CLK, "clk_sdio0_ciu", "sdio0_mux",
+		CLK_SET_RATE_PARENT, 0x9c, 1, 0, },
+	/* EMMC */
+	{ HISTB_MMC_BIU_CLK, "clk_mmc_biu", "200m",
+		CLK_SET_RATE_PARENT, 0xa0, 0, 0, },
+	{ HISTB_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux",
+		CLK_SET_RATE_PARENT, 0xa0, 1, 0, },
+	/* Ethernet */
+	{ HI3798MV200_GMAC_CLK, "clk_gmac", "75m",
+		CLK_SET_RATE_PARENT, 0xcc, 2, 0, },
+	{ HI3798MV200_GMACIF_CLK, "clk_gmacif", NULL,
+		CLK_SET_RATE_PARENT, 0xcc, 0, 0, },
+	{ HI3798MV200_FEMAC_CLK, "clk_femac", "eth_mux",
+		CLK_SET_RATE_PARENT, 0xd0, 1, 0, },
+	{ HI3798MV200_FEMACIF_CLK, "clk_femacif", NULL,
+		CLK_SET_RATE_PARENT, 0xd0, 0, 0, },
+	{ HI3798MV200_FEPHY_CLK, "clk_fephy", NULL,
+		0, 0x388, 0, 0, },
+	/* COMBPHY */
+	{ HISTB_COMBPHY0_CLK, "clk_combphy0", "combphy0_mux",
+		CLK_SET_RATE_PARENT, 0x188, 0, 0, },
+	/* USB2 */
+	{ HISTB_USB2_BUS_CLK, "clk_u2_bus", "clk_ahb",
+		CLK_SET_RATE_PARENT, 0xb8, 0, 0, },
+	{ HISTB_USB2_PHY_CLK, "clk_u2_phy", "60m",
+		CLK_SET_RATE_PARENT, 0xb8, 4, 0, },
+	{ HISTB_USB2_12M_CLK, "clk_u2_12m", "12m",
+		CLK_SET_RATE_PARENT, 0xb8, 2, 0 },
+	{ HISTB_USB2_48M_CLK, "clk_u2_48m", "48m",
+		CLK_SET_RATE_PARENT, 0xb8, 1, 0 },
+	{ HISTB_USB2_UTMI_CLK, "clk_u2_utmi", "60m",
+		CLK_SET_RATE_PARENT, 0xb8, 5, 0 },
+	{ HISTB_USB2_OTG_UTMI_CLK, "clk_u2_otg_utmi", "60m",
+		CLK_SET_RATE_PARENT, 0xb8, 3, 0 },
+	{ HISTB_USB2_PHY1_REF_CLK, "clk_u2_phy1_ref", "24m",
+		CLK_SET_RATE_PARENT, 0xbc, 0, 0 },
+	{ HISTB_USB2_PHY2_REF_CLK, "clk_u2_phy2_ref", "24m",
+		CLK_SET_RATE_PARENT, 0xbc, 2, 0 },
+	/* USB3 */
+	{ HISTB_USB3_BUS_CLK, "clk_u3_bus", NULL,
+		CLK_SET_RATE_PARENT, 0xb0, 0, 0 },
+	{ HISTB_USB3_UTMI_CLK, "clk_u3_utmi", NULL,
+		CLK_SET_RATE_PARENT, 0xb0, 4, 0 },
+	{ HISTB_USB3_PIPE_CLK, "clk_u3_pipe", NULL,
+		CLK_SET_RATE_PARENT, 0xb0, 3, 0 },
+	{ HISTB_USB3_SUSPEND_CLK, "clk_u3_suspend", NULL,
+		CLK_SET_RATE_PARENT, 0xb0, 2, 0 },
+	{ HISTB_USB3_BUS_CLK1, "clk_u3_bus1", NULL,
+		CLK_SET_RATE_PARENT, 0xb0, 16, 0 },
+	{ HISTB_USB3_UTMI_CLK1, "clk_u3_utmi1", NULL,
+		CLK_SET_RATE_PARENT, 0xb0, 20, 0 },
+	{ HISTB_USB3_PIPE_CLK1, "clk_u3_pipe1", NULL,
+		CLK_SET_RATE_PARENT, 0xb0, 19, 0 },
+	{ HISTB_USB3_SUSPEND_CLK1, "clk_u3_suspend1", NULL,
+		CLK_SET_RATE_PARENT, 0xb0, 18, 0 },
+	/* Watchdog */
+	{ HISTB_WDG0_CLK, "clk_wdg0", "24m",
+		CLK_SET_RATE_PARENT, 0x178, 0, 0 },
+	/* SDIO1 */
+	{ HISTB_SDIO1_BIU_CLK, "clk_sdio1_biu", "200m",
+			CLK_SET_RATE_PARENT, 0x28c, 0, 0, },
+	{ HISTB_SDIO1_CIU_CLK, "clk_sdio1_ciu", "sdio1_mux",
+		CLK_SET_RATE_PARENT, 0x28c, 1, 0, },
+};
+
+static int hi3798mv200_clk_init(struct udevice *pdev)
+{
+	struct hisi_clock_data *data = dev_get_priv(pdev);
+
+	return hisi_clk_init(pdev, data);
+}
+
+static int hi3798mv200_clk_register(struct udevice *pdev)
+{
+	struct hisi_clock_data *clk_data = dev_get_priv(pdev);
+	int ret;
+
+	ret = hisi_clk_register_fixed_rate(NULL, hi3798mv200_fixed_rate_clks,
+					   ARRAY_SIZE(hi3798mv200_fixed_rate_clks));
+	if (ret)
+		return ret;
+
+	ret = hisi_clk_register_mux(NULL, hi3798mv200_mux_clks,
+				    ARRAY_SIZE(hi3798mv200_mux_clks), clk_data);
+	if (ret)
+		return ret;
+
+	return hisi_clk_register_gate(NULL, hi3798mv200_gate_clks,
+				      ARRAY_SIZE(hi3798mv200_gate_clks), clk_data);
+}
+
+static int hi3798mv200_clk_bind(struct udevice *pdev)
+{
+	ofnode node = dev_ofnode(pdev);
+
+	/* bind hisilicon_reset driver to our device tree node */
+	return device_bind_driver_to_node(pdev, "hisilicon_reset", "hisilicon_reset", node, NULL);
+};
+
+static const struct udevice_id hi3798mv200_crg_compat[] = {
+	{ .compatible = "hisilicon,hi3798mv200-crg", },
+	{ }
+};
+
+U_BOOT_DRIVER(hi3798mv200_crg) = {
+	.name		= "hi3798mv200_crg",
+	.id		= UCLASS_CLK,
+	.bind		= hi3798mv200_clk_bind,
+	.of_to_plat	= hi3798mv200_clk_init,
+	.priv_auto	= sizeof(struct hisi_clock_data),
+	.probe		= hi3798mv200_clk_register,
+	.of_match	= hi3798mv200_crg_compat,
+	.ops		= &ccf_clk_ops,
+};
diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c
new file mode 100644
index 0000000000..6bd7f9ef60
--- /dev/null
+++ b/drivers/clk/hisilicon/clk.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hisilicon clock driver
+ * Adapted from linux kernel
+ */
+
+#include <dm/read.h>
+#include <linux/clk-provider.h>
+#include "clk.h"
+
+static spinlock_t lock;
+
+int hisi_clk_init(struct udevice *dev, struct hisi_clock_data *data)
+{
+	data->base = dev_remap_addr(dev);
+	if (!data->base)
+		return -EINVAL;
+
+	return 0;
+}
+
+int hisi_clk_register_fixed_rate(struct device *dev, const struct hisi_fixed_rate_clock *clks,
+				 int nums)
+{
+	struct clk *clk;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		clk = clk_register_fixed_rate(dev, clks[i].name,
+					      clks[i].fixed_rate);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			goto err;
+		}
+		clk_dm(clks[i].id, clk);
+	}
+	return 0;
+
+err:
+	return PTR_ERR(clk);
+}
+
+int hisi_clk_register_mux(struct device *dev,
+			  const struct hisi_mux_clock *clks,
+			  int nums,
+			  const struct hisi_clock_data *data)
+{
+	struct clk *clk;
+	void __iomem *base = data->base;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		u32 mask = BIT(clks[i].width) - 1;
+
+		clk = clk_register_mux_table(dev, clks[i].name,
+					     clks[i].parent_names,
+					     clks[i].num_parents, clks[i].flags,
+					     base + clks[i].offset, clks[i].shift,
+					     mask, clks[i].mux_flags, clks[i].table);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			goto err;
+		}
+
+		clk_dm(clks[i].id, clk);
+	}
+	return 0;
+
+err:
+	return PTR_ERR(clk);
+}
+
+int hisi_clk_register_gate(struct device *dev, const struct hisi_gate_clock *clks,
+			   int nums, const struct hisi_clock_data *data)
+{
+	struct clk *clk;
+	void __iomem *base = data->base;
+	int i;
+
+	for (i = 0; i < nums; i++) {
+		clk = clk_register_gate(dev, clks[i].name,
+					clks[i].parent_name,
+					clks[i].flags,
+					base + clks[i].offset,
+					clks[i].bit_idx,
+					clks[i].gate_flags,
+					&lock);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to register clock %s\n",
+			       __func__, clks[i].name);
+			goto err;
+		}
+
+		clk_dm(clks[i].id, clk);
+	}
+	return 0;
+
+err:
+	return PTR_ERR(clk);
+}
diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h
new file mode 100644
index 0000000000..67c889025d
--- /dev/null
+++ b/drivers/clk/hisilicon/clk.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Adapted from linux kernel, some functionalities are removed to keep it simple.
+ */
+
+#ifndef	__HISI_CLK_H
+#define	__HISI_CLK_H
+
+#include <linux/types.h>
+
+struct udevice;
+struct device;
+
+struct hisi_clock_data {
+	void __iomem		*base;
+};
+
+struct hisi_fixed_rate_clock {
+	unsigned int		id;
+	char			*name;
+	unsigned long		fixed_rate;
+};
+
+struct hisi_mux_clock {
+	unsigned int		id;
+	const char		*name;
+	const char		*const *parent_names;
+	u8			num_parents;
+	unsigned long		flags;
+	unsigned long		offset;
+	u8			shift;
+	u8			width;
+	u8			mux_flags;
+	u32			*table;
+};
+
+struct hisi_gate_clock {
+	unsigned int		id;
+	const char		*name;
+	const char		*parent_name;
+	unsigned long		flags;
+	unsigned long		offset;
+	u8			bit_idx;
+	u8			gate_flags;
+};
+
+int hisi_clk_init(struct udevice *dev, struct hisi_clock_data *data);
+int hisi_clk_register_fixed_rate(struct device *dev, const struct hisi_fixed_rate_clock *fixed,
+				 int nums);
+int hisi_clk_register_mux(struct device *dev, const struct hisi_mux_clock *mux,
+			  int nums, const struct hisi_clock_data *data);
+int hisi_clk_register_gate(struct device *dev, const struct hisi_gate_clock *gate,
+			   int nums, const struct hisi_clock_data *data);
+
+#endif	/* __HISI_CLK_H */
diff --git a/include/dt-bindings/clock/histb-clock.h b/include/dt-bindings/clock/histb-clock.h
index 8a05790d1a..929e2a4488 100644
--- a/include/dt-bindings/clock/histb-clock.h
+++ b/include/dt-bindings/clock/histb-clock.h
@@ -74,6 +74,9 @@
 #define HISTB_SDIO1_CIU_CLK		52
 #define HISTB_SDIO1_DRV_CLK		53
 #define HISTB_SDIO1_SAMPLE_CLK		54
+#define HISTB_ETH0_PHY_CLK		55
+#define HISTB_ETH1_PHY_CLK		56
+#define HISTB_WDG0_CLK			57
 
 /* Hi3798MV200 specific clocks */
 
@@ -82,6 +85,7 @@
 #define HI3798MV200_GMACIF_CLK		HISTB_ETH0_MACIF_CLK
 #define HI3798MV200_FEMAC_CLK		HISTB_ETH1_MAC_CLK
 #define HI3798MV200_FEMACIF_CLK		HISTB_ETH1_MACIF_CLK
+#define HI3798MV200_FEPHY_CLK		HISTB_ETH1_PHY_CLK
 
 /* clocks provided by mcu CRG */
 #define HISTB_MCE_CLK			1

-- 
2.43.0



More information about the U-Boot mailing list