clk: add composite clk support
authorPeng Fan <peng.fan@nxp.com>
Wed, 31 Jul 2019 07:01:54 +0000 (07:01 +0000)
committerLukasz Majewski <lukma@denx.de>
Wed, 31 Jul 2019 07:20:51 +0000 (09:20 +0200)
Import clk composite clk support from Linux Kernel 5.1-rc5

Signed-off-by: Peng Fan <peng.fan@nxp.com>
drivers/clk/Kconfig
drivers/clk/Makefile
drivers/clk/clk-composite.c [new file with mode: 0644]
include/linux/clk-provider.h

index 5e92446c18c786690f6fda1a96b256957e143c16..a3f0171b45fb5ec60361f25711ded00a12191418 100644 (file)
@@ -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_IMX6Q || SANDBOX_CLK_CCF
@@ -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)
index 39154eca59c3212389fbeea3205ae85f4c8094cb..68aabe1ca99ab1fadfa7be240f6cf940f58a9627 100644 (file)
@@ -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 += analogbits/
 obj-y += imx/
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
new file mode 100644 (file)
index 0000000..a5626c3
--- /dev/null
@@ -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,
+};
index 522e73e8517473e58dc99eecf684751318131657..b9547736ee20414edd716391690c763de2a6d4fd 100644 (file)
@@ -137,6 +137,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, const char *name,
                 const char *parent_name);