summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/clk/tegra/Kconfig7
-rw-r--r--drivers/clk/tegra/Makefile1
-rw-r--r--drivers/clk/tegra/tegra-car-clk.c103
3 files changed, 111 insertions, 0 deletions
diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig
index 659fe022c2..ce80b1ff3e 100644
--- a/drivers/clk/tegra/Kconfig
+++ b/drivers/clk/tegra/Kconfig
@@ -1,3 +1,10 @@
+config TEGRA_CAR_CLOCK
+ bool "Enable Tegra CAR-based clock driver"
+ depends on TEGRA_CAR
+ help
+ Enable support for manipulating Tegra's on-SoC clocks via direct
+ register access to the Tegra CAR (Clock And Reset controller).
+
config TEGRA186_CLOCK
bool "Enable Tegra186 BPMP-based clock driver"
depends on TEGRA186_BPMP
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index f32998ccc2..0fcc5205a7 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -2,4 +2,5 @@
#
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_TEGRA_CAR_CLOCK) += tegra-car-clk.o
obj-$(CONFIG_TEGRA186_CLOCK) += tegra186-clk.o
diff --git a/drivers/clk/tegra/tegra-car-clk.c b/drivers/clk/tegra/tegra-car-clk.c
new file mode 100644
index 0000000000..b8a2c82a25
--- /dev/null
+++ b/drivers/clk/tegra/tegra-car-clk.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch-tegra/clk_rst.h>
+
+static int tegra_car_clk_request(struct clk *clk)
+{
+ debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev,
+ clk->id);
+
+ /*
+ * Note that the first PERIPH_ID_COUNT clock IDs (where the value
+ * varies per SoC) are the peripheral clocks, which use a numbering
+ * scheme that matches HW registers 1:1. There are other clock IDs
+ * beyond this that are assigned arbitrarily by the Tegra CAR DT
+ * binding. Due to the implementation of this driver, it currently
+ * only supports the peripheral IDs.
+ */
+ if (clk->id >= PERIPH_ID_COUNT)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int tegra_car_clk_free(struct clk *clk)
+{
+ debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev,
+ clk->id);
+
+ return 0;
+}
+
+static ulong tegra_car_clk_get_rate(struct clk *clk)
+{
+ enum clock_id parent;
+
+ debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev,
+ clk->id);
+
+ parent = clock_get_periph_parent(clk->id);
+ return clock_get_periph_rate(clk->id, parent);
+}
+
+static ulong tegra_car_clk_set_rate(struct clk *clk, ulong rate)
+{
+ enum clock_id parent;
+
+ debug("%s(clk=%p, rate=%lu) (dev=%p, id=%lu)\n", __func__, clk, rate,
+ clk->dev, clk->id);
+
+ parent = clock_get_periph_parent(clk->id);
+ return clock_adjust_periph_pll_div(clk->id, parent, rate, NULL);
+}
+
+static int tegra_car_clk_enable(struct clk *clk)
+{
+ debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev,
+ clk->id);
+
+ clock_enable(clk->id);
+
+ return 0;
+}
+
+static int tegra_car_clk_disable(struct clk *clk)
+{
+ debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev,
+ clk->id);
+
+ clock_disable(clk->id);
+
+ return 0;
+}
+
+static struct clk_ops tegra_car_clk_ops = {
+ .request = tegra_car_clk_request,
+ .free = tegra_car_clk_free,
+ .get_rate = tegra_car_clk_get_rate,
+ .set_rate = tegra_car_clk_set_rate,
+ .enable = tegra_car_clk_enable,
+ .disable = tegra_car_clk_disable,
+};
+
+static int tegra_car_clk_probe(struct udevice *dev)
+{
+ debug("%s(dev=%p)\n", __func__, dev);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(tegra_car_clk) = {
+ .name = "tegra_car_clk",
+ .id = UCLASS_CLK,
+ .probe = tegra_car_clk_probe,
+ .ops = &tegra_car_clk_ops,
+};