diff options
author | Stephen Warren <swarren@nvidia.com> | 2016-09-13 10:45:59 -0600 |
---|---|---|
committer | Tom Warren <twarren@nvidia.com> | 2016-09-27 09:11:02 -0700 |
commit | 4a332d3ee770bd6b633fd3abba741451b17156bc (patch) | |
tree | 9243d95843a8de86ba987194b4253a12972c30f7 | |
parent | fe60f06dcdee82b41f47e56ccb99cced91f1d2ac (diff) |
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/Kconfig | 7 | ||||
-rw-r--r-- | drivers/clk/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/tegra/tegra-car-clk.c | 103 |
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, +}; |