summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/clk/ti/Kconfig6
-rw-r--r--drivers/clk/ti/Makefile1
-rw-r--r--drivers/clk/ti/clk-ctrl.c154
3 files changed, 161 insertions, 0 deletions
diff --git a/drivers/clk/ti/Kconfig b/drivers/clk/ti/Kconfig
index 30959a316af..9e257a2eb7c 100644
--- a/drivers/clk/ti/Kconfig
+++ b/drivers/clk/ti/Kconfig
@@ -10,6 +10,12 @@ config CLK_TI_AM3_DPLL
This enables the DPLL clock drivers support on AM33XX SoCs. The DPLL
provides all interface clocks and functional clocks to the processor.
+config CLK_TI_CTRL
+ bool "TI OMAP4 clock controller"
+ depends on CLK && OF_CONTROL
+ help
+ This enables the clock controller driver support on TI's SoCs.
+
config CLK_TI_DIVIDER
bool "TI divider clock driver"
depends on CLK && OF_CONTROL && CLK_CCF
diff --git a/drivers/clk/ti/Makefile b/drivers/clk/ti/Makefile
index f8aa735c833..ed45f18311b 100644
--- a/drivers/clk/ti/Makefile
+++ b/drivers/clk/ti/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_ARCH_OMAP2PLUS) += clk.o
obj-$(CONFIG_CLK_TI_AM3_DPLL) += clk-am3-dpll.o clk-am3-dpll-x2.o
+obj-$(CONFIG_CLK_TI_CTRL) += clk-ctrl.o
obj-$(CONFIG_CLK_TI_DIVIDER) += clk-divider.o
obj-$(CONFIG_CLK_TI_GATE) += clk-gate.o
obj-$(CONFIG_CLK_TI_MUX) += clk-mux.o
diff --git a/drivers/clk/ti/clk-ctrl.c b/drivers/clk/ti/clk-ctrl.c
new file mode 100644
index 00000000000..940e8d6cafb
--- /dev/null
+++ b/drivers/clk/ti/clk-ctrl.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * OMAP clock controller support
+ *
+ * Copyright (C) 2020 Dario Binacchi <[email protected]>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <clk-uclass.h>
+#include <asm/arch-am33xx/clock.h>
+
+struct clk_ti_ctrl_offs {
+ fdt_addr_t start;
+ fdt_size_t end;
+};
+
+struct clk_ti_ctrl_priv {
+ int offs_num;
+ struct clk_ti_ctrl_offs *offs;
+};
+
+static int clk_ti_ctrl_check_offs(struct clk *clk, fdt_addr_t offs)
+{
+ struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev);
+ int i;
+
+ for (i = 0; i < priv->offs_num; i++) {
+ if (offs >= priv->offs[i].start && offs <= priv->offs[i].end)
+ return 0;
+ }
+
+ return -EFAULT;
+}
+
+static int clk_ti_ctrl_disable(struct clk *clk)
+{
+ struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev);
+ u32 *clk_modules[2] = { };
+ fdt_addr_t offs;
+ int err;
+
+ offs = priv->offs[0].start + clk->id;
+ err = clk_ti_ctrl_check_offs(clk, offs);
+ if (err) {
+ dev_err(clk->dev, "invalid offset: 0x%lx\n", offs);
+ return err;
+ }
+
+ clk_modules[0] = (u32 *)(offs);
+ dev_dbg(clk->dev, "module address=%p\n", clk_modules[0]);
+ do_disable_clocks(NULL, clk_modules, 1);
+ return 0;
+}
+
+static int clk_ti_ctrl_enable(struct clk *clk)
+{
+ struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev);
+ u32 *clk_modules[2] = { };
+ fdt_addr_t offs;
+ int err;
+
+ offs = priv->offs[0].start + clk->id;
+ err = clk_ti_ctrl_check_offs(clk, offs);
+ if (err) {
+ dev_err(clk->dev, "invalid offset: 0x%lx\n", offs);
+ return err;
+ }
+
+ clk_modules[0] = (u32 *)(offs);
+ dev_dbg(clk->dev, "module address=%p\n", clk_modules[0]);
+ do_enable_clocks(NULL, clk_modules, 1);
+ return 0;
+}
+
+static ulong clk_ti_ctrl_get_rate(struct clk *clk)
+{
+ return 0;
+}
+
+static int clk_ti_ctrl_of_xlate(struct clk *clk,
+ struct ofnode_phandle_args *args)
+{
+ if (args->args_count != 2) {
+ dev_err(clk->dev, "invaild args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args_count)
+ clk->id = args->args[0];
+ else
+ clk->id = 0;
+
+ dev_dbg(clk->dev, "name=%s, id=%ld\n", clk->dev->name, clk->id);
+ return 0;
+}
+
+static int clk_ti_ctrl_of_to_plat(struct udevice *dev)
+{
+ struct clk_ti_ctrl_priv *priv = dev_get_priv(dev);
+ fdt_size_t fdt_size;
+ int i, size;
+
+ size = dev_read_size(dev, "reg");
+ if (size < 0) {
+ dev_err(dev, "failed to get 'reg' size\n");
+ return size;
+ }
+
+ priv->offs_num = size / 2 / sizeof(u32);
+ dev_dbg(dev, "size=%d, regs_num=%d\n", size, priv->offs_num);
+
+ priv->offs = kmalloc_array(priv->offs_num, sizeof(*priv->offs),
+ GFP_KERNEL);
+ if (!priv->offs)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->offs_num; i++) {
+ priv->offs[i].start =
+ dev_read_addr_size_index(dev, i, &fdt_size);
+ if (priv->offs[i].start == FDT_ADDR_T_NONE) {
+ dev_err(dev, "failed to get offset %d\n", i);
+ return -EINVAL;
+ }
+
+ priv->offs[i].end = priv->offs[i].start + fdt_size;
+ dev_dbg(dev, "start=0x%08lx, end=0x%08lx\n",
+ priv->offs[i].start, priv->offs[i].end);
+ }
+
+ return 0;
+}
+
+static struct clk_ops clk_ti_ctrl_ops = {
+ .of_xlate = clk_ti_ctrl_of_xlate,
+ .enable = clk_ti_ctrl_enable,
+ .disable = clk_ti_ctrl_disable,
+ .get_rate = clk_ti_ctrl_get_rate,
+};
+
+static const struct udevice_id clk_ti_ctrl_ids[] = {
+ {.compatible = "ti,clkctrl"},
+ {},
+};
+
+U_BOOT_DRIVER(clk_ti_ctrl) = {
+ .name = "ti_ctrl_clk",
+ .id = UCLASS_CLK,
+ .of_match = clk_ti_ctrl_ids,
+ .ofdata_to_platdata = clk_ti_ctrl_of_to_plat,
+ .ops = &clk_ti_ctrl_ops,
+ .priv_auto = sizeof(struct clk_ti_ctrl_priv),
+};