u-boot/drivers/clk/sunxi/clk_sunxi.c
Andre Przywara d6cb09d89d clk: sunxi: add and use dummy gate clocks
Some devices enumerate various clocks in their DT, and many drivers
just blanketly try to enable all of them. This creates problems
since we only model a few gate clocks, and the clock driver outputs
a warning when a clock is not described:
=========
sunxi_set_gate: (CLK#3) unhandled
=========

Some clocks don't have an enable bit, or are already enabled in a
different way, so we might want to just ignore them.

Add a CCU_CLK_F_DUMMY_GATE flag that indicates that case, and define
a GATE_DUMMY macro that can be used in the clock description array.
Define a few clocks, used by some pinctrl devices, that way to suppress
the runtime warnings.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Samuel Holland <samuel@sholland.org>
2022-05-24 01:16:15 +01:00

91 lines
1.8 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018 Amarula Solutions.
* Author: Jagan Teki <jagan@amarulasolutions.com>
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <errno.h>
#include <log.h>
#include <reset.h>
#include <asm/io.h>
#include <clk/sunxi.h>
#include <linux/bitops.h>
#include <linux/log2.h>
static const struct ccu_clk_gate *priv_to_gate(struct ccu_priv *priv,
unsigned long id)
{
return &priv->desc->gates[id];
}
static int sunxi_set_gate(struct clk *clk, bool on)
{
struct ccu_priv *priv = dev_get_priv(clk->dev);
const struct ccu_clk_gate *gate = priv_to_gate(priv, clk->id);
u32 reg;
if ((gate->flags & CCU_CLK_F_DUMMY_GATE))
return 0;
if (!(gate->flags & CCU_CLK_F_IS_VALID)) {
printf("%s: (CLK#%ld) unhandled\n", __func__, clk->id);
return 0;
}
debug("%s: (CLK#%ld) off#0x%x, BIT(%d)\n", __func__,
clk->id, gate->off, ilog2(gate->bit));
reg = readl(priv->base + gate->off);
if (on)
reg |= gate->bit;
else
reg &= ~gate->bit;
writel(reg, priv->base + gate->off);
return 0;
}
static int sunxi_clk_enable(struct clk *clk)
{
return sunxi_set_gate(clk, true);
}
static int sunxi_clk_disable(struct clk *clk)
{
return sunxi_set_gate(clk, false);
}
struct clk_ops sunxi_clk_ops = {
.enable = sunxi_clk_enable,
.disable = sunxi_clk_disable,
};
int sunxi_clk_probe(struct udevice *dev)
{
struct ccu_priv *priv = dev_get_priv(dev);
struct clk_bulk clk_bulk;
struct reset_ctl_bulk rst_bulk;
int ret;
priv->base = dev_read_addr_ptr(dev);
if (!priv->base)
return -ENOMEM;
priv->desc = (const struct ccu_desc *)dev_get_driver_data(dev);
if (!priv->desc)
return -EINVAL;
ret = clk_get_bulk(dev, &clk_bulk);
if (!ret)
clk_enable_bulk(&clk_bulk);
ret = reset_get_bulk(dev, &rst_bulk);
if (!ret)
reset_deassert_bulk(&rst_bulk);
return 0;
}