Skip to content

Instantly share code, notes, and snippets.

@apritzel
Created January 8, 2019 11:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apritzel/db93dd06b4defb46504bccbfe4fc2c20 to your computer and use it in GitHub Desktop.
Save apritzel/db93dd06b4defb46504bccbfe4fc2c20 to your computer and use it in GitHub Desktop.
recursive implemenation of U-Boot clock driver for Allwinner SoCs
// 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 <asm/io.h>
#include <asm/arch/ccu.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 const struct ccu_clk_tree *priv_to_tree(struct ccu_priv *priv,
unsigned long id)
{
return &priv->desc->tree[id];
}
static int sunxi_get_parent_id(const struct ccu_clk_tree *tree, void *base)
{
u32 reg, idx;
reg = readl(base + tree->off);
idx = reg >> tree->mux.shift;
idx &= (1 << tree->mux.width) - 1;
return tree->parent[idx];
}
static ulong sunxi_apply_pll(struct ccu_priv *priv, ulong id, ulong parent_rate)
{
const struct ccu_clk_tree *tree = priv_to_tree(priv, id);
ulong rate;
unsigned int n, k;
u32 reg;
reg = readl(priv->base + tree->off);
n = reg >> tree->n.shift;
n &= (1 << tree->n.width) - 1;
n += tree->n.offset;
if (!n)
n++;
k = reg >> tree->k.shift;
k &= (1 << tree->k.width) - 1;
k += tree->k.offset;
if (!k)
k++;
rate = parent_rate * n * k;
if (tree->flags & CCU_CLK_F_POSTDIV)
rate /= tree->postdiv;
return rate;
}
static ulong sunxi_apply_div(struct ccu_priv *priv, ulong id, ulong parent_rate)
{
const struct ccu_clk_tree *tree = priv_to_tree(priv, id);
unsigned int m, p;
u32 reg;
reg = readl(priv->base + tree->off);
m = reg >> tree->m.shift;
m &= (1 << tree->m.width) - 1;
m += tree->m.offset;
if (!m)
m++;
p = reg >> tree->p.shift;
p &= (1 << tree->p.width) - 1;
return (parent_rate >> p) / m;
}
static ulong sunxi_calc_clk_rate(struct ccu_priv *priv, unsigned long clkid)
{
const struct ccu_clk_tree *tree = priv_to_tree(priv, clkid);
ulong rate = 0;
if (!(tree->flags & CCU_CLK_F_INIT_DONE)) {
printf("%s: (CLK#%ld) unhandled\n", __func__, clkid);
return 0;
}
switch (tree->type) {
case CCU_CLK_TYPE_MISC:
return sunxi_calc_clk_rate(priv, tree->parent[0]);
case CCU_CLK_TYPE_MP:
rate = sunxi_calc_clk_rate(priv,
sunxi_get_parent_id(tree, priv->base));
return sunxi_apply_div(priv, clkid, rate);
case CCU_CLK_TYPE_FIXED:
return tree->fixed_rate;
case CCU_CLK_TYPE_NK:
rate = sunxi_calc_clk_rate(priv,
sunxi_get_parent_id(tree, priv->base));
return sunxi_apply_pll(priv, clkid, rate);
default:
printf("%s: (TYPE#%d) unhandled\n", __func__, tree->type);
break;
}
return rate;
}
static ulong sunxi_clk_get_rate(struct clk *clk)
{
struct ccu_priv *priv = dev_get_priv(clk->dev);
return sunxi_calc_clk_rate(priv, clk->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_INIT_DONE)) {
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,
.get_rate = sunxi_clk_get_rate,
};
int sunxi_clk_probe(struct udevice *dev)
{
struct ccu_priv *priv = dev_get_priv(dev);
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;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment