[PATCH 5/9] clk: meson: Export a1 functions to common file

Sean Anderson <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot
Message-ID <[email protected]>
Export several useful functions from a1 to a common file. These will be
used for s4 support.

Signed-off-by: Sean Anderson <[email protected]>
---

 drivers/clk/meson/Makefile    |   1 +
 drivers/clk/meson/a1.c        | 473 ----------------------------------
 drivers/clk/meson/axg-ao.c    |   4 -
 drivers/clk/meson/axg.c       |   4 -
 drivers/clk/meson/clk_meson.c | 350 +++++++++++++++++++++++++
 drivers/clk/meson/clk_meson.h | 143 ++++++++++
 drivers/clk/meson/g12a-ao.c   |   4 -
 drivers/clk/meson/g12a.c      |   4 -
 drivers/clk/meson/gxbb.c      |   4 -
 9 files changed, 494 insertions(+), 493 deletions(-)
 create mode 100644 drivers/clk/meson/clk_meson.c

diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index c7a446e86c4..3d1dfcdb640 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -3,6 +3,7 @@
 # Copyright (c) 2019 Baylibre, SAS
 # Jerome Brunet <[email protected]>
 
+obj-y += clk_meson.o
 obj-$(CONFIG_CLK_MESON_GX) += gxbb.o
 obj-$(CONFIG_CLK_MESON_AXG) += axg.o
 obj-$(CONFIG_CLK_MESON_AXG) += axg-ao.o
diff --git a/drivers/clk/meson/a1.c b/drivers/clk/meson/a1.c
index 422cdf5f4d1..b6d1887c98a 100644
--- a/drivers/clk/meson/a1.c
+++ b/drivers/clk/meson/a1.c
@@ -34,138 +34,6 @@
 
 #define EXTERNAL_FIXPLL_IN		(NR_PLL_CLKS + 1)
 
-#define SET_PARM_VALUE(_priv, _parm, _val)				\
-	regmap_update_bits((_priv)->map, (_parm)->reg_off,		\
-			   SETPMASK((_parm)->width, (_parm)->shift),	\
-			   (_val) << (_parm)->shift)
-
-#define GET_PARM_VALUE(_priv, _parm)					\
-({									\
-	uint _reg;							\
-	regmap_read((_priv)->map, (_parm)->reg_off, &_reg);		\
-	PARM_GET((_parm)->width, (_parm)->shift, _reg);			\
-})
-
-struct meson_clk {
-	struct regmap *map;
-};
-
-/**
- * enum meson_clk_type - The type of clock
- * @MESON_CLK_ANY: Special value that matches any clock type
- * @MESON_CLK_GATE: This clock is a gate
- * @MESON_CLK_MUX: This clock is a multiplexer
- * @MESON_CLK_DIV: This clock is a configurable divider
- * @MESON_CLK_FIXED_DIV: This clock is a configurable divider
- * @MESON_CLK_EXTERNAL: This is an external clock from different clock provider
- * @MESON_CLK_PLL: This is a PLL
- */
-enum meson_clk_type {
-	MESON_CLK_ANY = 0,
-	MESON_CLK_GATE,
-	MESON_CLK_MUX,
-	MESON_CLK_DIV,
-	MESON_CLK_FIXED_DIV,
-	MESON_CLK_EXTERNAL,
-	MESON_CLK_PLL,
-};
-
-/**
- * struct meson_clk_info - The parameters defining a clock
- * @name: Name of the clock
- * @parm: Register bits description for muxes and dividers
- * @div: Fixed divider value
- * @parents: List of parent clock IDs
- * @type: Clock type
- */
-struct meson_clk_info {
-	const char *name;
-	union {
-		const struct parm *parm;
-		u8 div;
-	};
-	const unsigned int *parents;
-	const enum meson_clk_type type;
-};
-
-/**
- * struct meson_clk_data - Clocks supported by clock provider
- * @num_clocks: Number of clocks
- * @clocks: Array of clock descriptions
- *
- */
-struct meson_clk_data {
-	const u8 num_clocks;
-	const struct meson_clk_info **clocks;
-};
-
-/* Clock description initialization macros */
-
-/* A multiplexer */
-#define CLK_MUX(_name, _reg, _shift, _width, ...)			\
-	(&(struct meson_clk_info){					\
-		.parents = (const unsigned int[])__VA_ARGS__,		\
-		.parm = &(struct parm) {				\
-			.reg_off = (_reg),				\
-			.shift = (_shift),				\
-			.width = (_width),				\
-		},							\
-		.name = (_name),					\
-		.type = MESON_CLK_MUX,					\
-	})
-
-/* A divider with an integral divisor */
-#define CLK_DIV(_name, _reg, _shift, _width, _parent)			\
-	(&(struct meson_clk_info){					\
-		.parents = (const unsigned int[]) { (_parent) },	\
-		.parm = &(struct parm) {				\
-			.reg_off = (_reg),				\
-			.shift = (_shift),				\
-			.width = (_width),				\
-		},							\
-		.name = (_name),					\
-		.type = MESON_CLK_DIV,					\
-	})
-
-/* A fixed divider */
-#define CLK_DIV_FIXED(_name, _div, _parent)				\
-	(&(struct meson_clk_info){					\
-		.parents = (const unsigned int[]) { (_parent) },	\
-		.div = (_div),						\
-		.name = (_name),					\
-		.type = MESON_CLK_FIXED_DIV,				\
-	})
-
-/* An external clock */
-#define CLK_EXTERNAL(_name)						\
-	(&(struct meson_clk_info){					\
-		.name = (_name),					\
-		.parents = (const unsigned int[]) { -ENOENT },		\
-		.type = MESON_CLK_EXTERNAL,				\
-	})
-
-/* A clock gate */
-#define CLK_GATE(_name, _reg, _shift, _parent)				\
-	(&(struct meson_clk_info){					\
-		.parents = (const unsigned int[]) { (_parent) },	\
-		.parm = &(struct parm) {				\
-			.reg_off = (_reg),				\
-			.shift = (_shift),				\
-			.width = 1,					\
-		},							\
-		.name = (_name),					\
-		.type = MESON_CLK_GATE,					\
-	})
-
-/* A PLL clock */
-#define CLK_PLL(_name, _parent, ...)					\
-	(&(struct meson_clk_info){					\
-		.name = (_name),					\
-		.parents = (const unsigned int[]) { (_parent) },	\
-		.parm = (const struct parm[])__VA_ARGS__,		\
-		.type = MESON_CLK_PLL,					\
-	})
-
 /* A1 peripherals clocks */
 static const struct meson_clk_info *meson_clocks[] = {
 	[CLKID_SPIFC_SEL] = CLK_MUX("spifc_sel", A1_SPIFC_CLK_CTRL, 9, 2, {
@@ -302,254 +170,6 @@ static const struct meson_clk_info *meson_pll_clocks[] = {
 	),
 };
 
-static const struct meson_clk_info *meson_clk_get_info(struct clk *clk,
-						       enum meson_clk_type type)
-{
-	struct meson_clk_data *data = (void *)dev_get_driver_data(clk->dev);
-	const struct meson_clk_info *info;
-
-	if (clk->id >= data->num_clocks)
-		return ERR_PTR(-EINVAL);
-
-	info = data->clocks[clk->id];
-	if (!info)
-		return ERR_PTR(-ENOENT);
-
-	if (type != MESON_CLK_ANY && type != info->type)
-		return ERR_PTR(-EINVAL);
-
-	return info;
-}
-
-static ulong meson_clk_get_rate(struct clk *clk);
-
-static int meson_set_gate(struct clk *clk, bool on)
-{
-	struct meson_clk *priv = dev_get_priv(clk->dev);
-	const struct meson_clk_info *info;
-
-	debug("%s: %sabling %lu\n", __func__, on ? "en" : "dis", clk->id);
-
-	info = meson_clk_get_info(clk, MESON_CLK_ANY);
-	if (IS_ERR(info))
-		return PTR_ERR(info);
-
-	SET_PARM_VALUE(priv, info->parm, on);
-
-	return 0;
-}
-
-static int meson_clk_enable(struct clk *clk)
-{
-	return meson_set_gate(clk, true);
-}
-
-static int meson_clk_disable(struct clk *clk)
-{
-	return meson_set_gate(clk, false);
-}
-
-static ulong meson_div_get_rate(struct clk *clk)
-{
-	struct meson_clk *priv = dev_get_priv(clk->dev);
-	u16 n;
-	ulong rate;
-	const struct meson_clk_info *info;
-	struct clk parent;
-
-	info = meson_clk_get_info(clk, MESON_CLK_DIV);
-	if (IS_ERR(info))
-		return PTR_ERR(info);
-
-	/* Actual divider value is (field value + 1), hence the increment */
-	n = GET_PARM_VALUE(priv, info->parm) + 1;
-
-	parent.dev = clk->dev;
-	parent.id = info->parents[0];
-	rate = meson_clk_get_rate(&parent);
-
-	return rate / n;
-}
-
-static int meson_clk_get_parent(struct clk *clk)
-{
-	uint reg = 0;
-	struct meson_clk *priv = dev_get_priv(clk->dev);
-	const struct meson_clk_info *info;
-
-	info = meson_clk_get_info(clk, MESON_CLK_ANY);
-	if (IS_ERR(info))
-		return PTR_ERR(info);
-
-	/* For muxes we read currently selected parent from register,
-	 * for other types there is always only one element in parents array.
-	 */
-	if (info->type == MESON_CLK_MUX) {
-		reg = GET_PARM_VALUE(priv, info->parm);
-		if (IS_ERR_VALUE(reg))
-			return reg;
-	}
-
-	return info->parents[reg];
-}
-
-static ulong meson_pll_get_rate(struct clk *clk)
-{
-	struct meson_clk *priv = dev_get_priv(clk->dev);
-	const struct meson_clk_info *info;
-	const struct parm *pm, *pn;
-	ulong parent_rate_mhz;
-	struct clk parent;
-	u16 n, m;
-
-	info = meson_clk_get_info(clk, MESON_CLK_ANY);
-	if (IS_ERR(info))
-		return PTR_ERR(info);
-
-	pm = &info->parm[0];
-	pn = &info->parm[1];
-
-	n = GET_PARM_VALUE(priv, pn);
-	m = GET_PARM_VALUE(priv, pm);
-
-	if (n == 0)
-		return -EINVAL;
-
-	parent.dev = clk->dev;
-	parent.id = info->parents[0];
-	parent_rate_mhz = meson_clk_get_rate(&parent) / 1000000;
-
-	return parent_rate_mhz * m / n * 1000000;
-}
-
-static ulong meson_clk_get_rate(struct clk *clk)
-{
-	struct clk parent;
-	const struct meson_clk_info *info;
-
-	if (IS_ERR_VALUE(clk->id))
-		return clk->id;
-
-	info = meson_clk_get_info(clk, MESON_CLK_ANY);
-	if (IS_ERR(info))
-		return PTR_ERR(info);
-
-	switch (info->type) {
-	case MESON_CLK_PLL:
-		return meson_pll_get_rate(clk);
-	case MESON_CLK_GATE:
-	case MESON_CLK_MUX:
-		parent.dev = clk->dev;
-		parent.id = meson_clk_get_parent(clk);
-		return meson_clk_get_rate(&parent);
-	case MESON_CLK_DIV:
-		return meson_div_get_rate(clk);
-	case MESON_CLK_FIXED_DIV:
-		parent.dev = clk->dev;
-		parent.id = meson_clk_get_parent(clk);
-		return meson_clk_get_rate(&parent) / info->div;
-	case MESON_CLK_EXTERNAL: {
-		int ret;
-		struct clk external_clk;
-
-		ret = clk_get_by_name(clk->dev, info->name, &external_clk);
-		if (ret)
-			return ret;
-
-		return clk_get_rate(&external_clk);
-	}
-	default:
-		return -EINVAL;
-	}
-}
-
-/* This implements rate propagation for dividers placed after multiplexer:
- *  ---------|\
- *     ..... | |---DIV--
- *  ---------|/
- */
-static ulong meson_composite_set_rate(struct clk *clk, ulong rate)
-{
-	unsigned int i, best_div_val;
-	unsigned long best_delta, best_parent;
-	const struct meson_clk_info *div;
-	const struct meson_clk_info *mux;
-	struct meson_clk *priv = dev_get_priv(clk->dev);
-	struct clk mux_clk;
-
-	div = meson_clk_get_info(clk, MESON_CLK_DIV);
-	if (IS_ERR(div))
-		return PTR_ERR(div);
-
-	mux_clk.dev = clk->dev;
-	mux_clk.id = div->parents[0];
-	mux = meson_clk_get_info(&mux_clk, MESON_CLK_MUX);
-	if (IS_ERR(mux))
-		return PTR_ERR(mux);
-
-	best_parent = -EINVAL;
-	best_delta = ULONG_MAX;
-	for (i = 0; i < (1 << mux->parm->width); i++) {
-		unsigned long parent_rate, delta;
-		unsigned int div_val;
-		struct clk parent = {
-			.dev = clk->dev,
-			.id =  mux->parents[i],
-		};
-
-		parent_rate = meson_clk_get_rate(&parent);
-		if (IS_ERR_VALUE(parent_rate))
-			continue;
-
-		/* If overflow, try to use max divider value */
-		div_val = min(DIV_ROUND_CLOSEST(parent_rate, rate),
-			      (1UL << div->parm->width));
-
-		delta = abs(rate - (parent_rate / div_val));
-		if (delta < best_delta) {
-			best_delta = delta;
-			best_div_val = div_val;
-			best_parent = i;
-		}
-	}
-
-	if (IS_ERR_VALUE(best_parent))
-		return best_parent;
-
-	SET_PARM_VALUE(priv, mux->parm, best_parent);
-	/* Divider is set to (field value + 1), hence the decrement */
-	SET_PARM_VALUE(priv, div->parm, best_div_val - 1);
-
-	return 0;
-}
-
-static ulong meson_mux_set_rate(struct clk *clk, ulong rate)
-{
-	int i;
-	ulong ret = -EINVAL;
-	struct meson_clk *priv = dev_get_priv(clk->dev);
-	const struct meson_clk_info *info;
-
-	info = meson_clk_get_info(clk, MESON_CLK_MUX);
-	if (IS_ERR(info))
-		return PTR_ERR(info);
-
-	for (i = 0; i < (1 << info->parm->width); i++) {
-		struct clk parent = {
-			.dev = clk->dev,
-			.id = info->parents[i],
-		};
-
-		ret = clk_set_rate(&parent, rate);
-		if (!ret) {
-			SET_PARM_VALUE(priv, info->parm, i);
-			break;
-		}
-	}
-
-	return ret;
-}
-
 /* Rate propagation is implemented for a subcection of a clock tree, that is
  * required at boot stage.
  */
@@ -575,32 +195,6 @@ static ulong meson_clk_set_rate(struct clk *clk, ulong rate)
 	return -EINVAL;
 }
 
-static int meson_clk_set_parent(struct clk *clk, struct clk *parent)
-{
-	unsigned int i, parent_index;
-	struct meson_clk *priv = dev_get_priv(clk->dev);
-	const struct meson_clk_info *info;
-
-	info = meson_clk_get_info(clk, MESON_CLK_MUX);
-	if (IS_ERR(info))
-		return PTR_ERR(info);
-
-	parent_index = -EINVAL;
-	for (i = 0; i < (1 << info->parm->width); i++) {
-		if (parent->id == info->parents[i]) {
-			parent_index = i;
-			break;
-		}
-	}
-
-	if (IS_ERR_VALUE(parent_index))
-		return parent_index;
-
-	SET_PARM_VALUE(priv, info->parm, parent_index);
-
-	return 0;
-}
-
 static int meson_clk_probe(struct udevice *dev)
 {
 	struct meson_clk *priv = dev_get_priv(dev);
@@ -630,73 +224,6 @@ static const struct udevice_id meson_clk_ids[] = {
 	{ }
 };
 
-#if IS_ENABLED(CONFIG_CMD_CLK)
-static const char *meson_clk_get_name(struct clk *clk)
-{
-	const struct meson_clk_info *info;
-
-	info = meson_clk_get_info(clk, MESON_CLK_ANY);
-
-	return IS_ERR(info) ? "unknown" : info->name;
-}
-
-static int meson_clk_dump_single(struct clk *clk)
-{
-	const struct meson_clk_info *info;
-	struct meson_clk *priv;
-	unsigned long rate;
-	char *state, frequency[80];
-	struct clk parent;
-
-	priv = dev_get_priv(clk->dev);
-
-	info = meson_clk_get_info(clk, MESON_CLK_ANY);
-	if (IS_ERR(info) || !info->name)
-		return -EINVAL;
-
-	rate = clk_get_rate(clk);
-	if (IS_ERR_VALUE(rate))
-		sprintf(frequency, "unknown");
-	else
-		sprintf(frequency, "%lu", rate);
-
-	if (info->type == MESON_CLK_GATE)
-		state = GET_PARM_VALUE(priv, info->parm) ? "enabled" : "disabled";
-	else
-		state = "N/A";
-
-	parent.dev = clk->dev;
-	parent.id = meson_clk_get_parent(clk);
-	printf("%15s%20s%20s%15s\n",
-	       info->name,
-	       frequency,
-	       meson_clk_get_name(&parent),
-	       state);
-
-	return 0;
-}
-
-static void meson_clk_dump(struct udevice *dev)
-{
-	int i;
-	struct meson_clk_data *data;
-	const char *sep = "--------------------";
-
-	printf("%s:\n", dev->name);
-	printf("%.15s%s%s%.15s\n", sep, sep, sep, sep);
-	printf("%15s%20s%20s%15s\n", "clk", "frequency", "parent", "state");
-	printf("%.15s%s%s%.15s\n", sep, sep, sep, sep);
-
-	data = (struct meson_clk_data *)dev_get_driver_data(dev);
-	for (i = 0; i < data->num_clocks; i++) {
-		meson_clk_dump_single(&(struct clk){
-			.dev = dev,
-			.id = i
-		});
-	}
-}
-#endif
-
 static struct clk_ops meson_clk_ops = {
 	.disable	= meson_clk_disable,
 	.enable		= meson_clk_enable,
diff --git a/drivers/clk/meson/axg-ao.c b/drivers/clk/meson/axg-ao.c
index 3215391006e..5f897d89e9b 100644
--- a/drivers/clk/meson/axg-ao.c
+++ b/drivers/clk/meson/axg-ao.c
@@ -10,10 +10,6 @@
 
 #include "clk_meson.h"
 
-struct meson_clk {
-	struct regmap *map;
-};
-
 #define AO_CLK_GATE0		0x40
 #define AO_SAR_CLK		0x90
 
diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c
index e20d3feaade..3b5f4af9f4c 100644
--- a/drivers/clk/meson/axg.c
+++ b/drivers/clk/meson/axg.c
@@ -20,10 +20,6 @@
 
 #define XTAL_RATE 24000000
 
-struct meson_clk {
-	struct regmap *map;
-};
-
 static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id);
 
 static struct meson_gate gates[] = {
diff --git a/drivers/clk/meson/clk_meson.c b/drivers/clk/meson/clk_meson.c
new file mode 100644
index 00000000000..de5aa5ae3c9
--- /dev/null
+++ b/drivers/clk/meson/clk_meson.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2023 SberDevices, Inc.
+ * Author: Igor Prusov <[email protected]>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <regmap.h>
+
+#include "clk_meson.h"
+
+static const struct meson_clk_info *meson_clk_get_info(struct clk *clk,
+						       enum meson_clk_type type)
+{
+	struct meson_clk_data *data = (void *)dev_get_driver_data(clk->dev);
+	const struct meson_clk_info *info;
+
+	if (clk->id >= data->num_clocks)
+		return ERR_PTR(-EINVAL);
+
+	info = data->clocks[clk->id];
+	if (!info)
+		return ERR_PTR(-ENOENT);
+
+	if (type != MESON_CLK_ANY && type != info->type)
+		return ERR_PTR(-EINVAL);
+
+	return info;
+}
+
+static int meson_set_gate(struct clk *clk, bool on)
+{
+	struct meson_clk *priv = dev_get_priv(clk->dev);
+	const struct meson_clk_info *info;
+
+	debug("%s: %sabling %lu\n", __func__, on ? "en" : "dis", clk->id);
+
+	info = meson_clk_get_info(clk, MESON_CLK_ANY);
+	if (IS_ERR(info))
+		return PTR_ERR(info);
+
+	SET_PARM_VALUE(priv, info->parm, on);
+
+	return 0;
+}
+
+int meson_clk_enable(struct clk *clk)
+{
+	return meson_set_gate(clk, true);
+}
+
+int meson_clk_disable(struct clk *clk)
+{
+	return meson_set_gate(clk, false);
+}
+
+static ulong meson_div_get_rate(struct clk *clk)
+{
+	struct meson_clk *priv = dev_get_priv(clk->dev);
+	u16 n;
+	ulong rate;
+	const struct meson_clk_info *info;
+	struct clk parent;
+
+	info = meson_clk_get_info(clk, MESON_CLK_DIV);
+	if (IS_ERR(info))
+		return PTR_ERR(info);
+
+	/* Actual divider value is (field value + 1), hence the increment */
+	n = GET_PARM_VALUE(priv, info->parm) + 1;
+
+	parent.dev = clk->dev;
+	parent.id = info->parents[0];
+	rate = meson_clk_get_rate(&parent);
+
+	return rate / n;
+}
+
+int meson_clk_get_parent(struct clk *clk)
+{
+	uint reg = 0;
+	struct meson_clk *priv = dev_get_priv(clk->dev);
+	const struct meson_clk_info *info;
+
+	info = meson_clk_get_info(clk, MESON_CLK_ANY);
+	if (IS_ERR(info))
+		return PTR_ERR(info);
+
+	/* For muxes we read currently selected parent from register,
+	 * for other types there is always only one element in parents array.
+	 */
+	if (info->type == MESON_CLK_MUX) {
+		reg = GET_PARM_VALUE(priv, info->parm);
+		if (IS_ERR_VALUE(reg))
+			return reg;
+	}
+
+	return info->parents[reg];
+}
+
+static ulong meson_pll_get_rate(struct clk *clk)
+{
+	struct meson_clk *priv = dev_get_priv(clk->dev);
+	const struct meson_clk_info *info;
+	const struct parm *pm, *pn;
+	ulong parent_rate_mhz;
+	struct clk parent;
+	u16 n, m;
+
+	info = meson_clk_get_info(clk, MESON_CLK_ANY);
+	if (IS_ERR(info))
+		return PTR_ERR(info);
+
+	pm = &info->parm[0];
+	pn = &info->parm[1];
+
+	n = GET_PARM_VALUE(priv, pn);
+	m = GET_PARM_VALUE(priv, pm);
+
+	if (n == 0)
+		return -EINVAL;
+
+	parent.dev = clk->dev;
+	parent.id = info->parents[0];
+	parent_rate_mhz = meson_clk_get_rate(&parent) / 1000000;
+
+	return parent_rate_mhz * m / n * 1000000;
+}
+
+ulong meson_clk_get_rate(struct clk *clk)
+{
+	struct clk parent;
+	const struct meson_clk_info *info;
+
+	if (IS_ERR_VALUE(clk->id))
+		return clk->id;
+
+	info = meson_clk_get_info(clk, MESON_CLK_ANY);
+	if (IS_ERR(info))
+		return PTR_ERR(info);
+
+	switch (info->type) {
+	case MESON_CLK_PLL:
+		return meson_pll_get_rate(clk);
+	case MESON_CLK_GATE:
+	case MESON_CLK_MUX:
+		parent.dev = clk->dev;
+		parent.id = meson_clk_get_parent(clk);
+		return meson_clk_get_rate(&parent);
+	case MESON_CLK_DIV:
+		return meson_div_get_rate(clk);
+	case MESON_CLK_FIXED_DIV:
+		parent.dev = clk->dev;
+		parent.id = meson_clk_get_parent(clk);
+		return meson_clk_get_rate(&parent) / info->div;
+	case MESON_CLK_EXTERNAL: {
+		int ret;
+		struct clk external_clk;
+
+		ret = clk_get_by_name(clk->dev, info->name, &external_clk);
+		if (ret)
+			return ret;
+
+		return clk_get_rate(&external_clk);
+	}
+	default:
+		return -EINVAL;
+	}
+}
+
+/* This implements rate propagation for dividers placed after multiplexer:
+ *  ---------|\
+ *     ..... | |---DIV--
+ *  ---------|/
+ */
+ulong meson_composite_set_rate(struct clk *clk, ulong rate)
+{
+	unsigned int i, best_div_val;
+	unsigned long best_delta, best_parent;
+	const struct meson_clk_info *div;
+	const struct meson_clk_info *mux;
+	struct meson_clk *priv = dev_get_priv(clk->dev);
+	struct clk mux_clk;
+
+	div = meson_clk_get_info(clk, MESON_CLK_DIV);
+	if (IS_ERR(div))
+		return PTR_ERR(div);
+
+	mux_clk.dev = clk->dev;
+	mux_clk.id = div->parents[0];
+	mux = meson_clk_get_info(&mux_clk, MESON_CLK_MUX);
+	if (IS_ERR(mux))
+		return PTR_ERR(mux);
+
+	best_parent = -EINVAL;
+	best_delta = ULONG_MAX;
+	for (i = 0; i < (1 << mux->parm->width); i++) {
+		unsigned long parent_rate, delta;
+		unsigned int div_val;
+		struct clk parent = {
+			.dev = clk->dev,
+			.id =  mux->parents[i],
+		};
+
+		parent_rate = meson_clk_get_rate(&parent);
+		if (IS_ERR_VALUE(parent_rate))
+			continue;
+
+		/* If overflow, try to use max divider value */
+		div_val = min(DIV_ROUND_CLOSEST(parent_rate, rate),
+			      (1UL << div->parm->width));
+
+		delta = abs(rate - (parent_rate / div_val));
+		if (delta < best_delta) {
+			best_delta = delta;
+			best_div_val = div_val;
+			best_parent = i;
+		}
+	}
+
+	if (IS_ERR_VALUE(best_parent))
+		return best_parent;
+
+	SET_PARM_VALUE(priv, mux->parm, best_parent);
+	/* Divider is set to (field value + 1), hence the decrement */
+	SET_PARM_VALUE(priv, div->parm, best_div_val - 1);
+
+	return 0;
+}
+
+ulong meson_mux_set_rate(struct clk *clk, ulong rate)
+{
+	int i;
+	ulong ret = -EINVAL;
+	struct meson_clk *priv = dev_get_priv(clk->dev);
+	const struct meson_clk_info *info;
+
+	info = meson_clk_get_info(clk, MESON_CLK_MUX);
+	if (IS_ERR(info))
+		return PTR_ERR(info);
+
+	for (i = 0; i < (1 << info->parm->width); i++) {
+		struct clk parent = {
+			.dev = clk->dev,
+			.id = info->parents[i],
+		};
+
+		ret = clk_set_rate(&parent, rate);
+		if (!ret) {
+			SET_PARM_VALUE(priv, info->parm, i);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int meson_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	unsigned int i, parent_index;
+	struct meson_clk *priv = dev_get_priv(clk->dev);
+	const struct meson_clk_info *info;
+
+	info = meson_clk_get_info(clk, MESON_CLK_MUX);
+	if (IS_ERR(info))
+		return PTR_ERR(info);
+
+	parent_index = -EINVAL;
+	for (i = 0; i < (1 << info->parm->width); i++) {
+		if (parent->id == info->parents[i]) {
+			parent_index = i;
+			break;
+		}
+	}
+
+	if (IS_ERR_VALUE(parent_index))
+		return parent_index;
+
+	SET_PARM_VALUE(priv, info->parm, parent_index);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_CMD_CLK)
+static const char *meson_clk_get_name(struct clk *clk)
+{
+	const struct meson_clk_info *info;
+
+	info = meson_clk_get_info(clk, MESON_CLK_ANY);
+
+	return IS_ERR(info) ? "unknown" : info->name;
+}
+
+static int meson_clk_dump_single(struct clk *clk)
+{
+	const struct meson_clk_info *info;
+	struct meson_clk *priv;
+	unsigned long rate;
+	char *state, frequency[80];
+	struct clk parent;
+
+	priv = dev_get_priv(clk->dev);
+
+	info = meson_clk_get_info(clk, MESON_CLK_ANY);
+	if (IS_ERR(info) || !info->name)
+		return -EINVAL;
+
+	rate = clk_get_rate(clk);
+	if (IS_ERR_VALUE(rate))
+		sprintf(frequency, "unknown");
+	else
+		sprintf(frequency, "%lu", rate);
+
+	if (info->type == MESON_CLK_GATE)
+		state = GET_PARM_VALUE(priv, info->parm) ? "enabled" : "disabled";
+	else
+		state = "N/A";
+
+	parent.dev = clk->dev;
+	parent.id = meson_clk_get_parent(clk);
+	printf("%15s%20s%20s%15s\n",
+	       info->name,
+	       frequency,
+	       meson_clk_get_name(&parent),
+	       state);
+
+	return 0;
+}
+
+void meson_clk_dump(struct udevice *dev)
+{
+	int i;
+	struct meson_clk_data *data;
+	const char *sep = "--------------------";
+
+	printf("%s:\n", dev->name);
+	printf("%.15s%s%s%.15s\n", sep, sep, sep, sep);
+	printf("%15s%20s%20s%15s\n", "clk", "frequency", "parent", "state");
+	printf("%.15s%s%s%.15s\n", sep, sep, sep, sep);
+
+	data = (struct meson_clk_data *)dev_get_driver_data(dev);
+	for (i = 0; i < data->num_clocks; i++) {
+		meson_clk_dump_single(&(struct clk){
+			.dev = dev,
+			.id = i
+		});
+	}
+}
+#endif
diff --git a/drivers/clk/meson/clk_meson.h b/drivers/clk/meson/clk_meson.h
index ef72a416c15..990e9434126 100644
--- a/drivers/clk/meson/clk_meson.h
+++ b/drivers/clk/meson/clk_meson.h
@@ -39,10 +39,153 @@ struct parm {
 #define PARM_SET(width, shift, reg, val)                                \
 	(((reg) & CLRPMASK(width, shift)) | ((val) << (shift)))
 
+#define SET_PARM_VALUE(_priv, _parm, _val)				\
+	regmap_update_bits((_priv)->map, (_parm)->reg_off,		\
+			   SETPMASK((_parm)->width, (_parm)->shift),	\
+			   (_val) << (_parm)->shift)
+
+#define GET_PARM_VALUE(_priv, _parm)					\
+({									\
+	uint _reg;							\
+	regmap_read((_priv)->map, (_parm)->reg_off, &_reg);		\
+	PARM_GET((_parm)->width, (_parm)->shift, _reg);			\
+})
+
+struct meson_clk {
+	struct regmap *map;
+};
+
+/**
+ * enum meson_clk_type - The type of clock
+ * @MESON_CLK_ANY: Special value that matches any clock type
+ * @MESON_CLK_GATE: This clock is a gate
+ * @MESON_CLK_MUX: This clock is a multiplexer
+ * @MESON_CLK_DIV: This clock is a configurable divider
+ * @MESON_CLK_FIXED_DIV: This clock is a configurable divider
+ * @MESON_CLK_EXTERNAL: This is an external clock from different clock provider
+ * @MESON_CLK_PLL: This is a PLL
+ */
+enum meson_clk_type {
+	MESON_CLK_ANY = 0,
+	MESON_CLK_GATE,
+	MESON_CLK_MUX,
+	MESON_CLK_DIV,
+	MESON_CLK_FIXED_DIV,
+	MESON_CLK_EXTERNAL,
+	MESON_CLK_PLL,
+};
+
+/**
+ * struct meson_clk_info - The parameters defining a clock
+ * @name: Name of the clock
+ * @parm: Register bits description for muxes and dividers
+ * @div: Fixed divider value
+ * @parents: List of parent clock IDs
+ * @type: Clock type
+ */
+struct meson_clk_info {
+	const char *name;
+	union {
+		const struct parm *parm;
+		u8 div;
+	};
+	const unsigned int *parents;
+	const enum meson_clk_type type;
+};
+
+/**
+ * struct meson_clk_data - Clocks supported by clock provider
+ * @num_clocks: Number of clocks
+ * @clocks: Array of clock descriptions
+ *
+ */
+struct meson_clk_data {
+	const u8 num_clocks;
+	const struct meson_clk_info **clocks;
+};
+
+/* Clock description initialization macros */
+
+/* A multiplexer */
+#define CLK_MUX(_name, _reg, _shift, _width, ...)			\
+	(&(struct meson_clk_info){					\
+		.parents = (const unsigned int[])__VA_ARGS__,		\
+		.parm = &(struct parm) {				\
+			.reg_off = (_reg),				\
+			.shift = (_shift),				\
+			.width = (_width),				\
+		},							\
+		.name = (_name),					\
+		.type = MESON_CLK_MUX,					\
+	})
+
+/* A divider with an integral divisor */
+#define CLK_DIV(_name, _reg, _shift, _width, _parent)			\
+	(&(struct meson_clk_info){					\
+		.parents = (const unsigned int[]) { (_parent) },	\
+		.parm = &(struct parm) {				\
+			.reg_off = (_reg),				\
+			.shift = (_shift),				\
+			.width = (_width),				\
+		},							\
+		.name = (_name),					\
+		.type = MESON_CLK_DIV,					\
+	})
+
+/* A fixed divider */
+#define CLK_DIV_FIXED(_name, _div, _parent)				\
+	(&(struct meson_clk_info){					\
+		.parents = (const unsigned int[]) { (_parent) },	\
+		.div = (_div),						\
+		.name = (_name),					\
+		.type = MESON_CLK_FIXED_DIV,				\
+	})
+
+/* An external clock */
+#define CLK_EXTERNAL(_name)						\
+	(&(struct meson_clk_info){					\
+		.name = (_name),					\
+		.parents = (const unsigned int[]) { -ENOENT },		\
+		.type = MESON_CLK_EXTERNAL,				\
+	})
+
+/* A clock gate */
+#define CLK_GATE(_name, _reg, _shift, _parent)				\
+	(&(struct meson_clk_info){					\
+		.parents = (const unsigned int[]) { (_parent) },	\
+		.parm = &(struct parm) {				\
+			.reg_off = (_reg),				\
+			.shift = (_shift),				\
+			.width = 1,					\
+		},							\
+		.name = (_name),					\
+		.type = MESON_CLK_GATE,					\
+	})
+
+/* A PLL clock */
+#define CLK_PLL(_name, _parent, ...)					\
+	(&(struct meson_clk_info){					\
+		.name = (_name),					\
+		.parents = (const unsigned int[]) { (_parent) },	\
+		.parm = (const struct parm[])__VA_ARGS__,		\
+		.type = MESON_CLK_PLL,					\
+	})
+
 /* MPLL Parameters */
 
 #define SDM_DEN 16384
 #define N2_MIN  4
 #define N2_MAX  511
 
+int meson_clk_enable(struct clk *clk);
+int meson_clk_disable(struct clk *clk);
+int meson_clk_get_parent(struct clk *clk);
+ulong meson_clk_get_rate(struct clk *clk);
+ulong meson_composite_set_rate(struct clk *clk, ulong rate);
+ulong meson_mux_set_rate(struct clk *clk, ulong rate);
+int meson_clk_set_parent(struct clk *clk, struct clk *parent);
+#if IS_ENABLED(CONFIG_CMD_CLK)
+void meson_clk_dump(struct udevice *dev);
+#endif
+
 #endif
diff --git a/drivers/clk/meson/g12a-ao.c b/drivers/clk/meson/g12a-ao.c
index e89638d65b5..d9425f900f3 100644
--- a/drivers/clk/meson/g12a-ao.c
+++ b/drivers/clk/meson/g12a-ao.c
@@ -10,10 +10,6 @@
 
 #include "clk_meson.h"
 
-struct meson_clk {
-	struct regmap *map;
-};
-
 #define AO_CLK_GATE0		0x4c
 #define AO_SAR_CLK		0x90
 
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
index cc7bfe9e2a2..b1ba5881904 100644
--- a/drivers/clk/meson/g12a.c
+++ b/drivers/clk/meson/g12a.c
@@ -91,10 +91,6 @@
 
 #define XTAL_RATE 24000000
 
-struct meson_clk {
-	struct regmap *map;
-};
-
 static ulong meson_div_get_rate(struct clk *clk, unsigned long id);
 static ulong meson_div_set_rate(struct clk *clk, unsigned long id, ulong rate,
 				ulong current_rate);
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 24b5f9b5d40..62c1e0eed4f 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -70,10 +70,6 @@
 
 #define XTAL_RATE 24000000
 
-struct meson_clk {
-	struct regmap *map;
-};
-
 static ulong meson_div_get_rate(struct clk *clk, unsigned long id);
 static ulong meson_div_set_rate(struct clk *clk, unsigned long id, ulong rate,
 				ulong current_rate);
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.