clock: implement a driver for the Tegra CAR
authorStephen Warren <swarren@nvidia.com>
Tue, 13 Sep 2016 16:45:59 +0000 (10:45 -0600)
committerTom Warren <twarren@nvidia.com>
Tue, 27 Sep 2016 16:11:02 +0000 (09:11 -0700)
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>
drivers/clk/tegra/Kconfig
drivers/clk/tegra/Makefile
drivers/clk/tegra/tegra-car-clk.c [new file with mode: 0644]

index 659fe022c2affab2ce841a95bfe8c1d3ddaf7b1e..ce80b1ff3ea4ae5afa8c051716aef8af51e66b92 100644 (file)
@@ -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
index f32998ccc27d3548e9062333527249489ce829f8..0fcc5205a70bc9cb999419e17ff9075f15d62525 100644 (file)
@@ -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 (file)
index 0000000..b8a2c82
--- /dev/null
@@ -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,
+};