From: "Stefan Dösinger" <stefandoesinger@gmail.com>
To: Michael Turquette <mturquette@baylibre.com>,
Stephen Boyd <sboyd@kernel.org>, Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Philipp Zabel <p.zabel@pengutronix.de>,
Brian Masney <bmasney@redhat.com>
Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
"Stefan Dösinger" <stefandoesinger@gmail.com>
Subject: [PATCH RFC v5 05/12] clk: zte: Add Clock registration infrastructure.
Date: Sun, 28 Jun 2026 22:59:00 +0300 [thread overview]
Message-ID: <20260628-zx29clk-v5-5-79ff044e4192@gmail.com> (raw)
In-Reply-To: <20260628-zx29clk-v5-0-79ff044e4192@gmail.com>
The next patches will implement the regmap clocks and PLL driver. The
actual hardware specific clock listing will live in a separate module.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Version 5:
*) Pass the static clk data instead of calling get_match_data to prepare
for operating as an MFD child.
*) Don't use devm_kzalloc to allocate the auxiliary_device
structure. I guess Sashiko is right, and that's what "Because once the
device is placed on the bus the parent driver can not tell what other
code may have a reference to this data" is trying to dell me.
*) Fix error check for device_node_to_regmap.
---
MAINTAINERS | 1 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/zte/Kconfig | 17 ++++
drivers/clk/zte/Makefile | 5 ++
drivers/clk/zte/clk-regmap.c | 30 +++++++
drivers/clk/zte/clk-zx.c | 192 +++++++++++++++++++++++++++++++++++++++++++
drivers/clk/zte/clk-zx.h | 81 ++++++++++++++++++
drivers/clk/zte/pll-zx.c | 19 +++++
9 files changed, 347 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 57af566030db..297c15a2c860 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3871,6 +3871,7 @@ F: Documentation/devicetree/bindings/clock/zte,zx297520v3-lspcrm.yaml
F: Documentation/devicetree/bindings/soc/zte/
F: arch/arm/boot/dts/zte/
F: arch/arm/mach-zte/
+F: drivers/clk/zte/
F: drivers/soc/zte/
F: include/dt-bindings/clock/zte,zx297520v3-clk.h
F: include/dt-bindings/reset/zte,zx297520v3-reset.h
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 1717ce75a907..6f0a863951ca 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -545,6 +545,7 @@ source "drivers/clk/uniphier/Kconfig"
source "drivers/clk/visconti/Kconfig"
source "drivers/clk/x86/Kconfig"
source "drivers/clk/xilinx/Kconfig"
+source "drivers/clk/zte/Kconfig"
source "drivers/clk/zynqmp/Kconfig"
# Kunit test cases
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index cc108a75a900..13a5478f1112 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -167,5 +167,6 @@ ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_X86) += x86/
endif
obj-y += xilinx/
+obj-$(CONFIG_COMMON_CLK_ZTE) += zte/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
obj-$(CONFIG_COMMON_CLK_ZYNQMP) += zynqmp/
diff --git a/drivers/clk/zte/Kconfig b/drivers/clk/zte/Kconfig
new file mode 100644
index 000000000000..b7b65a2172a9
--- /dev/null
+++ b/drivers/clk/zte/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ZTE Clock Drivers
+#
+
+config COMMON_CLK_ZTE
+ tristate "Clock driver for ZTE SoCs"
+ depends on ARCH_ZTE || COMPILE_TEST
+ default ARCH_ZTE
+ select AUXILIARY_BUS
+ select MFD_SYSCON
+ help
+ This option selects common clock infrastructure for ZTE based SoCs.
+ You will need to enable one or more SoC specific drivers to make use
+ of this.
+
+ Enable this if you are building a kernel for a ZTE designed board.
diff --git a/drivers/clk/zte/Makefile b/drivers/clk/zte/Makefile
new file mode 100644
index 000000000000..27db07293165
--- /dev/null
+++ b/drivers/clk/zte/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_COMMON_CLK_ZTE) += clk-zte.o
+
+clk-zte-y += clk-zx.o pll-zx.o clk-regmap.o
diff --git a/drivers/clk/zte/clk-regmap.c b/drivers/clk/zte/clk-regmap.c
new file mode 100644
index 000000000000..7908f1562f63
--- /dev/null
+++ b/drivers/clk/zte/clk-regmap.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Copyright (c) 2026 Stefan Dösinger.
+ * Author: Stefan Dösinger <stefandoesinger@gmail.com>
+ */
+
+#include "clk-zx.h"
+
+int zx_clk_register_gates(struct device *dev, struct regmap *regmap,
+ const struct zx_gate_desc *desc, unsigned int num,
+ struct clk_hw_onecell_data *clocks)
+{
+ return -ENODEV;
+}
+
+int zx_clk_register_dividers(struct device *dev, struct regmap *regmap,
+ const struct zx_div_desc *desc, unsigned int num,
+ struct clk_hw_onecell_data *clocks)
+{
+ return -ENODEV;
+}
+
+int zx_clk_register_muxes(struct device *dev, struct regmap *regmap,
+ const struct zx_mux_desc *desc, unsigned int num,
+ struct clk_hw_onecell_data *clocks)
+{
+ return -ENODEV;
+}
diff --git a/drivers/clk/zte/clk-zx.c b/drivers/clk/zte/clk-zx.c
new file mode 100644
index 000000000000..d098243145ce
--- /dev/null
+++ b/drivers/clk/zte/clk-zx.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Stefan Dösinger
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#include "clk-zx.h"
+
+static void zx_adev_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+
+ kfree(adev);
+}
+
+static void zx_adev_unregister(void *data)
+{
+ struct auxiliary_device *adev = data;
+
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static void zx_delete_clk_provider(void *data)
+{
+ of_clk_del_provider(data);
+}
+
+static void zx_clk_disable_unprepare_put(void *data)
+{
+ clk_disable_unprepare(data);
+ clk_put(data);
+}
+
+int zx_clk_common_probe(struct device *dev, struct device_node *of_node,
+ const struct zx_clk_data *data)
+{
+ unsigned int public_clk_count = 1, highest_id = 0;
+ struct clk_hw_onecell_data *clocks;
+ struct auxiliary_device *adev;
+ struct regmap *map;
+ struct clk *clk;
+ unsigned int i;
+ int res;
+
+ map = device_node_to_regmap(of_node);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ for (i = 0; i < data->num_plls; ++i) {
+ if (data->plls[i].id) {
+ unsigned int last_idx = data->plls[i].id + data->plls[i].num_postdivs - 1;
+
+ if (last_idx > highest_id)
+ highest_id = last_idx;
+ public_clk_count += data->plls[i].num_postdivs;
+ }
+ }
+ for (i = 0; i < data->num_muxes; ++i) {
+ if (data->muxes[i].id) {
+ if (data->muxes[i].id > highest_id)
+ highest_id = data->muxes[i].id;
+ public_clk_count++;
+ }
+ }
+ for (i = 0; i < data->num_divs; ++i) {
+ if (data->divs[i].id) {
+ if (data->divs[i].id > highest_id)
+ highest_id = data->divs[i].id;
+ public_clk_count++;
+ }
+ }
+ for (i = 0; i < data->num_gates; ++i) {
+ if (data->gates[i].id) {
+ if (data->gates[i].id > highest_id)
+ highest_id = data->gates[i].id;
+ public_clk_count++;
+ }
+ }
+
+ if (WARN_ON(public_clk_count != highest_id + 1))
+ return -EINVAL;
+
+ clocks = devm_kzalloc(dev, struct_size(clocks, hws, public_clk_count), GFP_KERNEL);
+ if (!clocks)
+ return -ENOMEM;
+ clocks->num = public_clk_count;
+
+ for (i = 0; i < data->num_inputs_enable; ++i) {
+ clk = of_clk_get_by_name(of_node, data->inputs_enable[i]);
+ if (IS_ERR(clk)) {
+ return dev_err_probe(dev, PTR_ERR(clk), "Input clk %s failure\n",
+ data->inputs_enable[i]);
+ }
+
+ res = clk_prepare_enable(clk);
+ if (res) {
+ clk_put(clk);
+ return dev_err_probe(dev, PTR_ERR(clk), "Input clk %s enable failure\n",
+ data->inputs_enable[i]);
+ }
+ res = devm_add_action_or_reset(dev, zx_clk_disable_unprepare_put, clk);
+ if (res)
+ return res;
+ }
+ for (i = 0; i < data->num_inputs; ++i) {
+ /* FIXME: devm_get_clk_from_child doesn't do any tree traversal, so it works here
+ * whether "of_node" belongs to "dev" or a parent of "dev". Is it supposed to be
+ * used that way though?
+ */
+ clk = devm_get_clk_from_child(dev, of_node, data->inputs[i]);
+ if (IS_ERR(clk)) {
+ return dev_err_probe(dev, PTR_ERR(clk), "Input clk %s failure\n",
+ data->inputs[i]);
+ }
+ }
+
+ res = zx_clk_register_plls(dev, map, data->plls, data->num_plls, clocks);
+ if (res)
+ return res;
+
+ res = zx_clk_register_muxes(dev, map, data->muxes, data->num_muxes, clocks);
+ if (res)
+ return res;
+
+ res = zx_clk_register_dividers(dev, map, data->divs, data->num_divs, clocks);
+ if (res)
+ return res;
+
+ res = zx_clk_register_gates(dev, map, data->gates, data->num_gates, clocks);
+ if (res)
+ return res;
+
+ /* This is to catch holes in the tables rather than registration errors. The count vs
+ * highest ID should catch most static issues. This check here will trigger if an ID is
+ * reused by accident.
+ */
+ for (i = 1; i < public_clk_count; i++) {
+ if (WARN(!clocks->hws[i], "Clock %u not registered\n", i))
+ return -EINVAL;
+ }
+
+ res = of_clk_add_hw_provider(of_node, of_clk_hw_onecell_get, clocks);
+ if (res)
+ return res;
+ res = devm_add_action_or_reset(dev, zx_delete_clk_provider, of_node);
+ if (res)
+ return res;
+
+ if (!data->reset_auxdev_name)
+ return 0;
+
+ adev = kzalloc_obj(*adev);
+ if (!adev)
+ return -ENOMEM;
+
+ adev->name = data->reset_auxdev_name;
+ adev->dev.parent = dev;
+ adev->dev.release = zx_adev_release;
+ adev->dev.of_node = of_node;
+
+ res = auxiliary_device_init(adev);
+ if (res) {
+ dev_err_probe(dev, res, "Failed to init aux dev %s\n", adev->name);
+ goto adev_free;
+ }
+
+ res = auxiliary_device_add(adev);
+ if (res) {
+ dev_err_probe(dev, res, "Failed to add aux dev %s\n", adev->name);
+ goto adev_uninit;
+ }
+
+ return devm_add_action_or_reset(dev, zx_adev_unregister, adev);
+
+adev_uninit:
+ auxiliary_device_uninit(adev);
+adev_free:
+ kfree(adev);
+ return res;
+}
+EXPORT_SYMBOL_NS_GPL(zx_clk_common_probe, "ZTE_CLK");
+
+MODULE_AUTHOR("Stefan Dösinger <stefandoesinger@gmail.com>");
+MODULE_DESCRIPTION("ZTE common clock driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/zte/clk-zx.h b/drivers/clk/zte/clk-zx.h
new file mode 100644
index 000000000000..dabb71f27c16
--- /dev/null
+++ b/drivers/clk/zte/clk-zx.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 Stefan Dösinger
+ */
+
+#ifndef __DRV_CLK_ZX_H
+#define __DRV_CLK_ZX_H
+
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+struct zx_pll_desc {
+ unsigned int id;
+ const char *name;
+ const char * const *parents;
+ unsigned int num_parents;
+ unsigned long rate;
+ const unsigned int *postdivs;
+ unsigned int num_postdivs;
+ u16 reg;
+};
+
+struct zx_mux_desc {
+ unsigned int id;
+ const char *name;
+ const char * const *parents;
+ unsigned int num_parents;
+ u16 reg;
+ u8 shift, size;
+};
+
+struct zx_div_desc {
+ unsigned int id;
+ const char *name, *parent;
+ u16 reg;
+ u8 shift, size;
+};
+
+struct zx_gate_desc {
+ unsigned int id;
+ const char *name, *parent;
+ unsigned long flags;
+ u16 reg;
+ u8 shift;
+};
+
+int zx_clk_register_plls(struct device *dev, struct regmap *regmap,
+ const struct zx_pll_desc *desc, unsigned int num,
+ struct clk_hw_onecell_data *clocks);
+int zx_clk_register_muxes(struct device *dev, struct regmap *regmap,
+ const struct zx_mux_desc *desc, unsigned int num,
+ struct clk_hw_onecell_data *clocks);
+int zx_clk_register_dividers(struct device *dev, struct regmap *regmap,
+ const struct zx_div_desc *desc, unsigned int num,
+ struct clk_hw_onecell_data *clocks);
+int zx_clk_register_gates(struct device *dev, struct regmap *regmap,
+ const struct zx_gate_desc *desc, unsigned int num,
+ struct clk_hw_onecell_data *clocks);
+
+struct zx_clk_data {
+ const char * const *inputs_enable;
+ unsigned int num_inputs_enable;
+ const char * const *inputs;
+ unsigned int num_inputs;
+ const struct zx_pll_desc *plls;
+ unsigned int num_plls;
+ const struct zx_mux_desc *muxes;
+ unsigned int num_muxes;
+ const struct zx_div_desc *divs;
+ unsigned int num_divs;
+ const struct zx_gate_desc *gates;
+ unsigned int num_gates;
+ const char *reset_auxdev_name;
+};
+
+int zx_clk_common_probe(struct device *dev, struct device_node *of_node,
+ const struct zx_clk_data *data);
+
+#endif /* __DRV_CLK_ZX_H */
diff --git a/drivers/clk/zte/pll-zx.c b/drivers/clk/zte/pll-zx.c
new file mode 100644
index 000000000000..c0475d5441fb
--- /dev/null
+++ b/drivers/clk/zte/pll-zx.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Stefan Dösinger
+ */
+#include <linux/clk-provider.h>
+#include <linux/rational.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+#include <linux/clk.h>
+
+#include "clk-zx.h"
+
+int zx_clk_register_plls(struct device *dev, struct regmap *regmap,
+ const struct zx_pll_desc *desc, unsigned int num,
+ struct clk_hw_onecell_data *clocks)
+{
+ return -ENODEV;
+}
--
2.53.0
next prev parent reply other threads:[~2026-06-28 19:59 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-28 19:58 [PATCH RFC v5 00/12] ZTE zx297520v3 clock bindings and driver Stefan Dösinger
2026-06-28 19:58 ` [PATCH RFC v5 01/12] dt-bindings: soc: zte: Add zx297520v3 top clock and reset bindings Stefan Dösinger
2026-06-28 20:12 ` sashiko-bot
2026-06-28 19:58 ` [PATCH RFC v5 02/12] dt-bindings: soc: zte: Add zx297520v3 matrix " Stefan Dösinger
2026-06-28 20:09 ` sashiko-bot
2026-06-28 19:58 ` [PATCH RFC v5 03/12] dt-bindings: clk: zte: Add zx297520v3 LSP " Stefan Dösinger
2026-06-28 19:58 ` [PATCH RFC v5 04/12] mfd: zx297520v3: Add a clock and reset MFD driver Stefan Dösinger
2026-06-28 20:10 ` sashiko-bot
2026-06-28 19:59 ` Stefan Dösinger [this message]
2026-06-28 20:10 ` [PATCH RFC v5 05/12] clk: zte: Add Clock registration infrastructure sashiko-bot
2026-06-30 8:27 ` Philipp Zabel
2026-06-30 8:53 ` Stefan Dösinger
2026-07-02 9:01 ` Philipp Zabel
2026-06-28 19:59 ` [PATCH RFC v5 06/12] clk: zte: Add zx PLL support infrastructure Stefan Dösinger
2026-06-28 20:14 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 07/12] clk: zte: Add regmap based clocks Stefan Dösinger
2026-06-28 20:28 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 08/12] clk: zte: Introduce a driver for zx297520v3 top clocks Stefan Dösinger
2026-06-28 20:16 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 09/12] clk: zte: Introduce a driver for zx297520v3 matrix clocks Stefan Dösinger
2026-06-28 20:12 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 10/12] clk: zte: Introduce a driver for zx297520v3 LSP clocks and resets Stefan Dösinger
2026-06-28 20:18 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 11/12] reset: zte: Add a zx297520v3 reset driver Stefan Dösinger
2026-06-28 20:23 ` sashiko-bot
2026-06-30 8:45 ` Philipp Zabel
2026-06-28 19:59 ` [PATCH RFC v5 12/12] ARM: dts: zte: Declare zx297520v3 CRM device nodes Stefan Dösinger
2026-06-29 15:49 ` [PATCH RFC v5 00/12] ZTE zx297520v3 clock bindings and driver Conor Dooley
2026-07-01 17:22 ` Stefan Dösinger
2026-07-02 19:05 ` Conor Dooley
2026-07-02 19:26 ` Stefan Dösinger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260628-zx29clk-v5-5-79ff044e4192@gmail.com \
--to=stefandoesinger@gmail.com \
--cc=bmasney@redhat.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mturquette@baylibre.com \
--cc=p.zabel@pengutronix.de \
--cc=robh@kernel.org \
--cc=sboyd@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.