summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2016-09-13 10:45:59 -0600
committerTom Warren <twarren@nvidia.com>2016-09-27 09:11:02 -0700
commit4a332d3ee770bd6b633fd3abba741451b17156bc (patch)
tree9243d95843a8de86ba987194b4253a12972c30f7
parentfe60f06dcdee82b41f47e56ccb99cced91f1d2ac (diff)
downloadu-boot-imx-4a332d3ee770bd6b633fd3abba741451b17156bc.zip
u-boot-imx-4a332d3ee770bd6b633fd3abba741451b17156bc.tar.gz
u-boot-imx-4a332d3ee770bd6b633fd3abba741451b17156bc.tar.bz2
clock: implement a driver for the Tegra CAR
Implement a clock uclass driver for the Tegra CAR. This allows clients to use standard clock APIs on Tegra. This device is intended to be instantiated by the core Tegra CAR driver, rather than being instantiated directly from DT. The implementation uses the existing custom Tegra- specific clock APIs to avoid coupling the series with significant refactoring of the existing Tegra clock/clock code. The driver currently only supports peripheral clocks, and avoids support for other clocks such as PLLs and external clocks. This should be sufficient to convert over all Tegra peripheral drivers, and avoids a complex implementation which calls different Tegra-specific clock APIs based on the type of clock being manipulated. Signed-off-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Tom Warren <twarren@nvidia.com>
-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 659fe02..ce80b1f 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 f32998c..0fcc5205 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 0000000..b8a2c82
--- /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,
+};