clk: add composite clk support

Import clk composite clk support from Linux Kernel 5.1-rc5

Signed-off-by: Peng Fan <peng.fan@nxp.com>
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 5e92446..a3f0171 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -53,6 +53,13 @@
 	  Enable this option if you want to (re-)use the Linux kernel's Common
 	  Clock Framework [CCF] code in U-Boot's SPL.
 
+config SPL_CLK_COMPOSITE_CCF
+	bool "SPL Common Clock Framework [CCF] composite clk support "
+	depends on SPL_CLK_CCF
+	help
+	  Enable this option if you want to (re-)use the Linux kernel's Common
+	  Clock Framework [CCF] composite code in U-Boot's SPL.
+
 config CLK_CCF
 	bool "Common Clock Framework [CCF] support "
 	depends on CLK_IMX6Q || SANDBOX_CLK_CCF
@@ -60,6 +67,13 @@
 	  Enable this option if you want to (re-)use the Linux kernel's Common
 	  Clock Framework [CCF] code in U-Boot's clock driver.
 
+config CLK_COMPOSITE_CCF
+	bool "Common Clock Framework [CCF] composite clk support "
+	depends on CLK_CCF
+	help
+	  Enable this option if you want to (re-)use the Linux kernel's Common
+	  Clock Framework [CCF] composite code in U-Boot's clock driver.
+
 config CLK_STM32F
 	bool "Enable clock driver support for STM32F family"
 	depends on CLK && (STM32F7 || STM32F4)
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 39154ec..68aabe1 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -9,6 +9,7 @@
 obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o
 obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o clk-gate.o
 obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
+obj-$(CONFIG_$(SPL_TPL_)CLK_COMPOSITE_CCF) += clk-composite.o
 
 obj-y += analogbits/
 obj-y += imx/
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
new file mode 100644
index 0000000..a5626c3
--- /dev/null
+++ b/drivers/clk/clk-composite.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2013 NVIDIA CORPORATION.  All rights reserved.
+ * Copyright 2019 NXP
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <clk.h>
+
+#include "clk.h"
+
+#define UBOOT_DM_CLK_COMPOSITE "clk_composite"
+
+static u8 clk_composite_get_parent(struct clk *clk)
+{
+	struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
+		(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
+	struct clk *mux = composite->mux;
+
+	return clk_mux_get_parent(mux);
+}
+
+static int clk_composite_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
+		(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
+	const struct clk_ops *mux_ops = composite->mux_ops;
+	struct clk *mux = composite->mux;
+
+	return mux_ops->set_parent(mux, parent);
+}
+
+static unsigned long clk_composite_recalc_rate(struct clk *clk)
+{
+	struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
+		(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
+	const struct clk_ops *rate_ops = composite->rate_ops;
+	struct clk *rate = composite->rate;
+
+	return rate_ops->get_rate(rate);
+}
+
+static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate)
+{
+	struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
+		(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
+	const struct clk_ops *rate_ops = composite->rate_ops;
+	struct clk *clk_rate = composite->rate;
+
+	return rate_ops->set_rate(clk_rate, rate);
+}
+
+static int clk_composite_enable(struct clk *clk)
+{
+	struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
+		(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
+	const struct clk_ops *gate_ops = composite->gate_ops;
+	struct clk *gate = composite->gate;
+
+	return gate_ops->enable(gate);
+}
+
+static int clk_composite_disable(struct clk *clk)
+{
+	struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
+		(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
+	const struct clk_ops *gate_ops = composite->gate_ops;
+	struct clk *gate = composite->gate;
+
+	gate_ops->disable(gate);
+
+	return 0;
+}
+
+struct clk_ops clk_composite_ops = {
+	/* This will be set according to clk_register_composite */
+};
+
+struct clk *clk_register_composite(struct device *dev, const char *name,
+				   const char * const *parent_names,
+				   int num_parents, struct clk *mux,
+				   const struct clk_ops *mux_ops,
+				   struct clk *rate,
+				   const struct clk_ops *rate_ops,
+				   struct clk *gate,
+				   const struct clk_ops *gate_ops,
+				   unsigned long flags)
+{
+	struct clk *clk;
+	struct clk_composite *composite;
+	int ret;
+	struct clk_ops *composite_ops = &clk_composite_ops;
+
+	composite = kzalloc(sizeof(*composite), GFP_KERNEL);
+	if (!composite)
+		return ERR_PTR(-ENOMEM);
+
+	if (mux && mux_ops) {
+		composite->mux = mux;
+		composite->mux_ops = mux_ops;
+		if (mux_ops->set_parent)
+			composite_ops->set_parent = clk_composite_set_parent;
+		mux->data = (ulong)composite;
+	}
+
+	if (rate && rate_ops) {
+		if (!rate_ops->get_rate) {
+			clk = ERR_PTR(-EINVAL);
+			goto err;
+		}
+		composite_ops->get_rate = clk_composite_recalc_rate;
+
+		/* .set_rate requires either .round_rate or .determine_rate */
+		if (rate_ops->set_rate)
+			composite_ops->set_rate = clk_composite_set_rate;
+
+		composite->rate = rate;
+		composite->rate_ops = rate_ops;
+		rate->data = (ulong)composite;
+	}
+
+	if (gate && gate_ops) {
+		if (!gate_ops->enable || !gate_ops->disable) {
+			clk = ERR_PTR(-EINVAL);
+			goto err;
+		}
+
+		composite->gate = gate;
+		composite->gate_ops = gate_ops;
+		composite_ops->enable = clk_composite_enable;
+		composite_ops->disable = clk_composite_disable;
+		gate->data = (ulong)composite;
+	}
+
+	clk = &composite->clk;
+	ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name,
+			   parent_names[clk_composite_get_parent(clk)]);
+	if (ret) {
+		clk = ERR_PTR(ret);
+		goto err;
+	}
+
+	return clk;
+
+err:
+	kfree(composite);
+	return clk;
+}
+
+U_BOOT_DRIVER(clk_composite) = {
+	.name	= UBOOT_DM_CLK_COMPOSITE,
+	.id	= UCLASS_CLK,
+	.ops	= &clk_composite_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};