mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-19 03:08:31 +00:00
4a332d3ee7
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>
103 lines
2.3 KiB
C
103 lines
2.3 KiB
C
/*
|
|
* 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,
|
|
};
|