[U-Boot,i.MX8MM+CCF,38/41] clk: add composite clk support
diff mbox series

Message ID 20190430103056.32537-39-peng.fan@nxp.com
State Needs Review / ACK
Delegated to: Stefano Babic
Headers show
Series
  • i.MX8MM + CCF
Related show

Commit Message

Peng Fan April 30, 2019, 10:19 a.m. UTC
Import clk composite clk support from Linux Kernel 5.1-rc5

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/clk/Kconfig          |  14 ++++
 drivers/clk/Makefile         |   1 +
 drivers/clk/clk-composite.c  | 165 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk-provider.h |  22 ++++++
 4 files changed, 202 insertions(+)
 create mode 100644 drivers/clk/clk-composite.c

Patch
diff mbox series

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 9df3bc731a..3735e235f5 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -53,6 +53,13 @@  config SPL_CLK_CCF
 	  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
@@ -60,6 +67,13 @@  config CLK_CCF
 	  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 0bc8f7e5ce..6c71b0fc16 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -9,6 +9,7 @@  obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o
 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 += imx/
 obj-y += tegra/
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
new file mode 100644
index 0000000000..18adfd9850
--- /dev/null
+++ b/drivers/clk/clk-composite.c
@@ -0,0 +1,165 @@ 
+// 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_driver_data(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_driver_data(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_driver_data(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_driver_data(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_driver_data(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_driver_data(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, (ulong)clk,
+			   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,
+};
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index b2bed768b6..490713adb6 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -119,6 +119,28 @@  struct clk_fixed_rate {
 
 #define to_clk_fixed_rate(dev)	((struct clk_fixed_rate *)dev_get_platdata(dev))
 
+struct clk_composite {
+	struct clk	clk;
+	struct clk_ops	ops;
+
+	struct clk	*mux;
+	struct clk	*rate;
+	struct clk	*gate;
+
+	const struct clk_ops	*mux_ops;
+	const struct clk_ops	*rate_ops;
+	const struct clk_ops	*gate_ops;
+};
+
+#define to_clk_composite(_clk) container_of(_clk, struct clk_composite, clk)
+
+struct clk *clk_register_composite(struct device *dev, const char *name,
+		const char * const *parent_names, int num_parents,
+		struct clk *mux_clk, const struct clk_ops *mux_ops,
+		struct clk *rate_clk, const struct clk_ops *rate_ops,
+		struct clk *gate_clk, const struct clk_ops *gate_ops,
+		unsigned long flags);
+
 int clk_register(struct clk *clk, const char *drv_name,
 		 ulong drv_data, const char *name,
 		 const char *parent_name);