summaryrefslogtreecommitdiffstats
path: root/drivers/clk/st/clkgen-mux.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/st/clkgen-mux.c')
-rw-r--r--drivers/clk/st/clkgen-mux.c111
1 files changed, 111 insertions, 0 deletions
diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c
new file mode 100644
index 0000000000..596e939ad9
--- /dev/null
+++ b/drivers/clk/st/clkgen-mux.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * clkgen-mux.c: ST GEN-MUX Clock driver
+ *
+ * Copyright (C) 2014 STMicroelectronics (R&D) Limited
+ *
+ * Authors: Stephen Gallimore <stephen.gallimore@st.com>
+ * Pankaj Dev <pankaj.dev@st.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include "clkgen.h"
+
+static const char ** __init clkgen_mux_get_parents(struct device_node *np,
+ int *num_parents)
+{
+ const char **parents;
+ unsigned int nparents;
+
+ nparents = of_clk_get_parent_count(np);
+ if (WARN_ON(!nparents))
+ return ERR_PTR(-EINVAL);
+
+ parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
+ if (!parents)
+ return ERR_PTR(-ENOMEM);
+
+ *num_parents = of_clk_parent_fill(np, parents, nparents);
+ return parents;
+}
+
+struct clkgen_mux_data {
+ u32 offset;
+ u8 shift;
+ u8 width;
+ spinlock_t *lock;
+ unsigned long clk_flags;
+ u8 mux_flags;
+};
+
+static struct clkgen_mux_data stih407_a9_mux_data = {
+ .offset = 0x1a4,
+ .shift = 0,
+ .width = 2,
+ .lock = &clkgen_a9_lock,
+};
+
+static void __init st_of_clkgen_mux_setup(struct device_node *np,
+ struct clkgen_mux_data *data)
+{
+ struct clk *clk;
+ void __iomem *reg;
+ const char **parents;
+ int num_parents = 0;
+ struct device_node *parent_np;
+
+ /*
+ * First check for reg property within the node to keep backward
+ * compatibility, then if reg doesn't exist look at the parent node
+ */
+ reg = of_iomap(np, 0);
+ if (!reg) {
+ parent_np = of_get_parent(np);
+ reg = of_iomap(parent_np, 0);
+ of_node_put(parent_np);
+ if (!reg) {
+ pr_err("%s: Failed to get base address\n", __func__);
+ return;
+ }
+ }
+
+ parents = clkgen_mux_get_parents(np, &num_parents);
+ if (IS_ERR(parents)) {
+ pr_err("%s: Failed to get parents (%ld)\n",
+ __func__, PTR_ERR(parents));
+ goto err_parents;
+ }
+
+ clk = clk_register_mux(NULL, np->name, parents, num_parents,
+ data->clk_flags | CLK_SET_RATE_PARENT,
+ reg + data->offset,
+ data->shift, data->width, data->mux_flags,
+ data->lock);
+ if (IS_ERR(clk))
+ goto err;
+
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+
+ kfree(parents);
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+
+err:
+ kfree(parents);
+err_parents:
+ iounmap(reg);
+}
+
+static void __init st_of_clkgen_a9_mux_setup(struct device_node *np)
+{
+ st_of_clkgen_mux_setup(np, &stih407_a9_mux_data);
+}
+CLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux",
+ st_of_clkgen_a9_mux_setup);