u-boot/drivers/clk/ti/clk-am3-dpll.c
Dario Binacchi b0db69b4e1 dm: fix build errors generated by last merges
Something was wrong in the merge process into the mainline.
Some added patches access driver structure fields and functions that
have been modified by previous patches.
The patch renames:
 - dev_get_platdata to dev_get_plat
 - dev_get_uclass_platdata to dev_get_uclass_plat
 - ofdata_to_platdata to of_to_plat
 - plat_data_alloc_size to plat_auto
 - priv_auto_alloc_size to priv_auto
 - video_uc_platdata to video_uc_plat

Signed-off-by: Dario Binacchi <dariobin@libero.it>
2021-01-15 13:12:38 -05:00

268 lines
6.3 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* TI DPLL clock support
*
* Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
*
* Loosely based on Linux kernel drivers/clk/ti/dpll.c
*/
#include <common.h>
#include <clk.h>
#include <clk-uclass.h>
#include <div64.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <hang.h>
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
#include <asm/io.h>
struct clk_ti_am3_dpll_drv_data {
ulong max_rate;
};
struct clk_ti_am3_dpll_priv {
fdt_addr_t clkmode_reg;
fdt_addr_t idlest_reg;
fdt_addr_t clksel_reg;
struct clk clk_bypass;
struct clk clk_ref;
u16 last_rounded_mult;
u8 last_rounded_div;
ulong max_rate;
};
static ulong clk_ti_am3_dpll_round_rate(struct clk *clk, ulong rate)
{
struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
ulong ret, ref_rate, r;
int m, d, err_min, err;
int mult = INT_MAX, div = INT_MAX;
if (priv->max_rate && rate > priv->max_rate) {
dev_warn(clk->dev, "%ld is to high a rate, lowered to %ld\n",
rate, priv->max_rate);
rate = priv->max_rate;
}
ret = -EFAULT;
err = rate;
err_min = rate;
ref_rate = clk_get_rate(&priv->clk_ref);
for (d = 1; err_min && d <= 128; d++) {
for (m = 2; m <= 2047; m++) {
r = (ref_rate * m) / d;
err = abs(r - rate);
if (err < err_min) {
err_min = err;
ret = r;
mult = m;
div = d;
if (err == 0)
break;
} else if (r > rate) {
break;
}
}
}
priv->last_rounded_mult = mult;
priv->last_rounded_div = div;
dev_dbg(clk->dev, "rate=%ld, best_rate=%ld, mult=%d, div=%d\n", rate,
ret, mult, div);
return ret;
}
static ulong clk_ti_am3_dpll_set_rate(struct clk *clk, ulong rate)
{
struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
u32 v;
ulong round_rate;
round_rate = clk_ti_am3_dpll_round_rate(clk, rate);
if (IS_ERR_VALUE(round_rate))
return round_rate;
v = readl(priv->clksel_reg);
/* enter bypass mode */
clrsetbits_le32(priv->clkmode_reg, CM_CLKMODE_DPLL_DPLL_EN_MASK,
DPLL_EN_MN_BYPASS << CM_CLKMODE_DPLL_EN_SHIFT);
/* wait for bypass mode */
if (!wait_on_value(ST_DPLL_CLK_MASK, 0,
(void *)priv->idlest_reg, LDELAY))
dev_err(clk->dev, "failed bypassing dpll\n");
/* set M & N */
v &= ~CM_CLKSEL_DPLL_M_MASK;
v |= (priv->last_rounded_mult << CM_CLKSEL_DPLL_M_SHIFT) &
CM_CLKSEL_DPLL_M_MASK;
v &= ~CM_CLKSEL_DPLL_N_MASK;
v |= ((priv->last_rounded_div - 1) << CM_CLKSEL_DPLL_N_SHIFT) &
CM_CLKSEL_DPLL_N_MASK;
writel(v, priv->clksel_reg);
/* lock dpll */
clrsetbits_le32(priv->clkmode_reg, CM_CLKMODE_DPLL_DPLL_EN_MASK,
DPLL_EN_LOCK << CM_CLKMODE_DPLL_EN_SHIFT);
/* wait till the dpll locks */
if (!wait_on_value(ST_DPLL_CLK_MASK, ST_DPLL_CLK_MASK,
(void *)priv->idlest_reg, LDELAY)) {
dev_err(clk->dev, "failed locking dpll\n");
hang();
}
return round_rate;
}
static ulong clk_ti_am3_dpll_get_rate(struct clk *clk)
{
struct clk_ti_am3_dpll_priv *priv = dev_get_priv(clk->dev);
u64 rate;
u32 m, n, v;
/* Return bypass rate if DPLL is bypassed */
v = readl(priv->clkmode_reg);
v &= CM_CLKMODE_DPLL_EN_MASK;
v >>= CM_CLKMODE_DPLL_EN_SHIFT;
switch (v) {
case DPLL_EN_MN_BYPASS:
case DPLL_EN_LOW_POWER_BYPASS:
case DPLL_EN_FAST_RELOCK_BYPASS:
rate = clk_get_rate(&priv->clk_bypass);
dev_dbg(clk->dev, "rate=%lld\n", rate);
return rate;
}
v = readl(priv->clksel_reg);
m = v & CM_CLKSEL_DPLL_M_MASK;
m >>= CM_CLKSEL_DPLL_M_SHIFT;
n = v & CM_CLKSEL_DPLL_N_MASK;
n >>= CM_CLKSEL_DPLL_N_SHIFT;
rate = clk_get_rate(&priv->clk_ref) * m;
do_div(rate, n + 1);
dev_dbg(clk->dev, "rate=%lld\n", rate);
return rate;
}
const struct clk_ops clk_ti_am3_dpll_ops = {
.round_rate = clk_ti_am3_dpll_round_rate,
.get_rate = clk_ti_am3_dpll_get_rate,
.set_rate = clk_ti_am3_dpll_set_rate,
};
static int clk_ti_am3_dpll_remove(struct udevice *dev)
{
struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
int err;
err = clk_release_all(&priv->clk_bypass, 1);
if (err) {
dev_err(dev, "failed to release bypass clock\n");
return err;
}
err = clk_release_all(&priv->clk_ref, 1);
if (err) {
dev_err(dev, "failed to release reference clock\n");
return err;
}
return 0;
}
static int clk_ti_am3_dpll_probe(struct udevice *dev)
{
struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
int err;
err = clk_get_by_index(dev, 0, &priv->clk_ref);
if (err) {
dev_err(dev, "failed to get reference clock\n");
return err;
}
err = clk_get_by_index(dev, 1, &priv->clk_bypass);
if (err) {
dev_err(dev, "failed to get bypass clock\n");
return err;
}
return 0;
}
static int clk_ti_am3_dpll_of_to_plat(struct udevice *dev)
{
struct clk_ti_am3_dpll_priv *priv = dev_get_priv(dev);
struct clk_ti_am3_dpll_drv_data *data =
(struct clk_ti_am3_dpll_drv_data *)dev_get_driver_data(dev);
priv->max_rate = data->max_rate;
priv->clkmode_reg = dev_read_addr_index(dev, 0);
if (priv->clkmode_reg == FDT_ADDR_T_NONE) {
dev_err(dev, "failed to get clkmode register\n");
return -EINVAL;
}
dev_dbg(dev, "clkmode_reg=0x%08lx\n", priv->clkmode_reg);
priv->idlest_reg = dev_read_addr_index(dev, 1);
if (priv->idlest_reg == FDT_ADDR_T_NONE) {
dev_err(dev, "failed to get idlest register\n");
return -EINVAL;
}
dev_dbg(dev, "idlest_reg=0x%08lx\n", priv->idlest_reg);
priv->clksel_reg = dev_read_addr_index(dev, 2);
if (priv->clksel_reg == FDT_ADDR_T_NONE) {
dev_err(dev, "failed to get clksel register\n");
return -EINVAL;
}
dev_dbg(dev, "clksel_reg=0x%08lx\n", priv->clksel_reg);
return 0;
}
static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_data = {
.max_rate = 1000000000
};
static const struct clk_ti_am3_dpll_drv_data dpll_no_gate_j_type_data = {
.max_rate = 2000000000
};
static const struct clk_ti_am3_dpll_drv_data dpll_core_data = {
.max_rate = 1000000000
};
static const struct udevice_id clk_ti_am3_dpll_of_match[] = {
{.compatible = "ti,am3-dpll-core-clock",
.data = (ulong)&dpll_core_data},
{.compatible = "ti,am3-dpll-no-gate-clock",
.data = (ulong)&dpll_no_gate_data},
{.compatible = "ti,am3-dpll-no-gate-j-type-clock",
.data = (ulong)&dpll_no_gate_j_type_data},
{}
};
U_BOOT_DRIVER(clk_ti_am3_dpll) = {
.name = "ti_am3_dpll_clock",
.id = UCLASS_CLK,
.of_match = clk_ti_am3_dpll_of_match,
.of_to_plat = clk_ti_am3_dpll_of_to_plat,
.probe = clk_ti_am3_dpll_probe,
.remove = clk_ti_am3_dpll_remove,
.priv_auto = sizeof(struct clk_ti_am3_dpll_priv),
.ops = &clk_ti_am3_dpll_ops,
};