clk: rockchip: Add support for clk compensation

Change-Id: I099261a5906dd72dca15cbbf6acea16179c471ad
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
This commit is contained in:
Sugar Zhang
2020-12-24 17:07:30 +08:00
committed by Tao Huang
parent c74052035b
commit 8d9702cd43
3 changed files with 94 additions and 0 deletions

View File

@ -1,3 +1,8 @@
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
config ROCKCHIP_CLK_COMPENSATION
bool "Rockchip Clk Compensation"
help
Say y here to enable clk compensation(+/- 1000 ppm).
source "drivers/clk/rockchip/regmap/Kconfig" source "drivers/clk/rockchip/regmap/Kconfig"

View File

@ -25,6 +25,7 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/gcd.h> #include <linux/gcd.h>
#include <linux/clk/rockchip.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include "clk.h" #include "clk.h"
@ -1183,6 +1184,76 @@ static const struct clk_ops rockchip_rk3399_pll_clk_ops = {
.init = rockchip_rk3399_pll_init, .init = rockchip_rk3399_pll_init,
}; };
#ifdef CONFIG_ROCKCHIP_CLK_COMPENSATION
int rockchip_pll_clk_compensation(struct clk *clk, int ppm)
{
struct clk *parent = clk_get_parent(clk);
struct rockchip_clk_pll *pll;
static u32 frac, fbdiv;
unsigned long m, n;
bool negative;
u32 pllcon, pllcon0, pllcon2, fbdiv_mask, frac_mask, frac_shift;
u64 fracdiv;
if ((ppm > 1000) || (ppm < -1000))
return -EINVAL;
if (IS_ERR_OR_NULL(parent))
return -EINVAL;
pll = to_rockchip_clk_pll(__clk_get_hw(parent));
if (!pll)
return -EINVAL;
switch (pll->type) {
case pll_rk3036:
case pll_rk3328:
pllcon0 = RK3036_PLLCON(0);
pllcon2 = RK3036_PLLCON(2);
fbdiv_mask = RK3036_PLLCON0_FBDIV_MASK;
frac_mask = RK3036_PLLCON2_FRAC_MASK;
frac_shift = RK3036_PLLCON2_FRAC_SHIFT;
break;
case pll_rk3066:
return -EINVAL;
case pll_rk3399:
pllcon0 = RK3399_PLLCON(0);
pllcon2 = RK3399_PLLCON(2);
fbdiv_mask = RK3399_PLLCON0_FBDIV_MASK;
frac_mask = RK3399_PLLCON2_FRAC_MASK;
frac_shift = RK3399_PLLCON2_FRAC_SHIFT;
break;
default:
return -EINVAL;
}
negative = !!(ppm & BIT(31));
ppm = negative ? ~ppm + 1 : ppm;
if (!frac) {
frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask;
fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask;
}
m = frac * ppm;
n = ppm << 24;
fracdiv = negative ? frac - ((m / MHZ) + ((n / MHZ) * fbdiv)) :
frac + ((m / MHZ) + ((n / MHZ) * fbdiv));
if (fracdiv > frac_mask)
return -EINVAL;
pllcon = readl_relaxed(pll->reg_base + pllcon2);
pllcon &= ~(frac_mask << frac_shift);
pllcon |= fracdiv << frac_shift;
writel_relaxed(pllcon, pll->reg_base + pllcon2);
return 0;
}
EXPORT_SYMBOL(rockchip_pll_clk_compensation);
#endif
/* /*
* Common registering of pll clocks * Common registering of pll clocks
*/ */

View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2020 Rockchip Electronics Co. Ltd.
*/
#ifndef __LINUX_CLK_ROCKCHIP_H_
#define __LINUX_CLK_ROCKCHIP_H_
#ifdef CONFIG_ROCKCHIP_CLK_COMPENSATION
int rockchip_pll_clk_compensation(struct clk *clk, int ppm);
#else
static inline int rockchip_pll_clk_compensation(struct clk *clk, int ppm)
{
return -ENOSYS;
}
#endif
#endif /* __LINUX_CLK_ROCKCHIP_H_ */