summaryrefslogtreecommitdiffstats
path: root/drivers/clk/zynqmp/clk-mux-zynqmp.c
diff options
context:
space:
mode:
authorMichael Tretter <m.tretter@pengutronix.de>2019-03-12 11:20:53 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2019-03-18 08:50:38 +0100
commita3167da3657ae807f702f7f54a7054d756174558 (patch)
treea89d51f86b45069d97af459399fe32c582dc5c9a /drivers/clk/zynqmp/clk-mux-zynqmp.c
parentbb6ac1cfc09ae06840d162fcc8bf8d31248e3800 (diff)
downloadbarebox-a3167da3657ae807f702f7f54a7054d756174558.tar.gz
barebox-a3167da3657ae807f702f7f54a7054d756174558.tar.xz
clk: add ZynqMP clock driver
The ZynqMP has a platform management unit (PMU) that is responsible for managing the clocks. Therefore, the clock driver uses the firmware driver to control the clocks. The Barebox driver is based on the Linux driver, but contains deviations to make the driver more readable and more consistent. Signed-off-by: Michael Tretter <m.tretter@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/clk/zynqmp/clk-mux-zynqmp.c')
-rw-r--r--drivers/clk/zynqmp/clk-mux-zynqmp.c102
1 files changed, 102 insertions, 0 deletions
diff --git a/drivers/clk/zynqmp/clk-mux-zynqmp.c b/drivers/clk/zynqmp/clk-mux-zynqmp.c
new file mode 100644
index 0000000000..4003267bd1
--- /dev/null
+++ b/drivers/clk/zynqmp/clk-mux-zynqmp.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Zynq UltraScale+ MPSoC Clock Multiplexer
+ *
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
+ *
+ * Based on the Linux driver in drivers/clk/zynqmp/
+ *
+ * Copyright (C) 2016-2018 Xilinx
+ */
+
+#include <common.h>
+#include <linux/clk.h>
+#include <mach/firmware-zynqmp.h>
+
+#include "clk-zynqmp.h"
+
+#define CLK_MUX_READ_ONLY BIT(3)
+
+struct zynqmp_clk_mux {
+ struct clk clk;
+ u32 clk_id;
+ const struct zynqmp_eemi_ops *ops;
+};
+
+#define to_zynqmp_clk_mux(clk) \
+ container_of(clk, struct zynqmp_clk_mux, clk)
+
+static int zynqmp_clk_mux_get_parent(struct clk *clk)
+{
+ struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(clk);
+ u32 value;
+
+ mux->ops->clock_getparent(mux->clk_id, &value);
+
+ return value;
+}
+
+static int zynqmp_clk_mux_set_parent(struct clk *clk, u8 index)
+{
+ struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(clk);
+
+ return mux->ops->clock_setparent(mux->clk_id, index);
+}
+
+static const struct clk_ops zynqmp_clk_mux_ops = {
+ .set_rate = clk_parent_set_rate,
+ .round_rate = clk_parent_round_rate,
+ .get_parent = zynqmp_clk_mux_get_parent,
+ .set_parent = zynqmp_clk_mux_set_parent,
+};
+
+static const struct clk_ops zynqmp_clk_mux_ro_ops = {
+ .set_rate = clk_parent_set_rate,
+ .round_rate = clk_parent_round_rate,
+ .get_parent = zynqmp_clk_mux_get_parent,
+};
+
+struct clk *zynqmp_clk_register_mux(const char *name,
+ unsigned int clk_id,
+ const char **parents,
+ unsigned int num_parents,
+ const struct clock_topology *nodes)
+{
+ struct zynqmp_clk_mux *mux;
+ int ret;
+ int i;
+ const char **parent_names;
+
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ parent_names = kcalloc(num_parents, sizeof(*parent_names), GFP_KERNEL);
+ if (!parent_names) {
+ kfree(mux);
+ return ERR_PTR(-ENOMEM);
+ }
+ for (i = 0; i < num_parents; i++)
+ parent_names[i] = strdup(parents[i]);
+
+ mux->clk_id = clk_id;
+ mux->ops = zynqmp_pm_get_eemi_ops();
+
+ mux->clk.name = strdup(name);
+ if (nodes->type_flag & CLK_MUX_READ_ONLY)
+ mux->clk.ops = &zynqmp_clk_mux_ro_ops;
+ else
+ mux->clk.ops = &zynqmp_clk_mux_ops;
+ mux->clk.flags = nodes->flag | CLK_SET_RATE_PARENT;
+ mux->clk.parent_names = parent_names;
+ mux->clk.num_parents = num_parents;
+
+ ret = clk_register(&mux->clk);
+ if (ret) {
+ kfree(parent_names);
+ kfree(mux);
+ return ERR_PTR(ret);
+ }
+
+ return &mux->clk;
+}