mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-15 09:27:35 +00:00
6827aba348
Because the gate arrays are not given explicit sizes, the arrays are
only as large as the highest-numbered gate described in the driver.
However, only a subset of the CCU clocks are needed by U-Boot. So there
are valid clock specifiers with indexes greater than the size of the
arrays. Referencing any of these clocks causes out-of-bounds access.
Fix this by checking the identifier against the size of the array.
Fixes: 0d47bc7056
("clk: Add Allwinner A64 CLK driver")
Signed-off-by: Samuel Holland <samuel@sholland.org>
Reviewed-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
94 lines
1.9 KiB
C
94 lines
1.9 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)
|
|
{
|
|
if (id >= priv->desc->num_gates)
|
|
return NULL;
|
|
|
|
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 && (gate->flags & CCU_CLK_F_DUMMY_GATE))
|
|
return 0;
|
|
|
|
if (!gate || !(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;
|
|
}
|