Commit 609bd60b authored by Sean Anderson's avatar Sean Anderson Committed by Leo Yu-Chi Liang
Browse files

clk: k210: Rewrite to remove CCF



This is effectively a complete rewrite to remove all dependency on CCF.
The code is now smaller, and so is the binary. It also takes up less memory
at runtime (since we don't have to create 40 udevices). In general, I am
much happier with this driver as much of the complexity and late binding
has been removed.

The k210_*_params structs which were previously used to initialize CCF
clocks are now used as the complete configuration. Since we can write our
own division logic, we can now do away with several "half" clocks which
only existed to provide constant factors of two.

The clock IDs have been renumbered to remove unused clocks. This may not be
the last time they are renumbered, since we have diverged with Linux. There
are also still a few clocks left out which may need to be added back in.

In general, I have tried to leave out behavioral changes. However, there is
a small bugfix regarding ACLK. According to the technical reference manual,
its mux comes *after* its divider (which is present only for PLL0). This
would have required yet another intermediate clock to fix with CCF, but
with the new driver it is just 2 lines of code :)
Signed-off-by: default avatarSean Anderson <seanga2@gmail.com>
Reviewed-by: default avatarLeo Yu-Chi Liang <ycliang@andestech.com>
parent 6e33eba5
config CLK_K210
bool "Clock support for Kendryte K210"
depends on CLK && CLK_CCF && CLK_COMPOSITE_CCF
depends on CLK
help
This enables support clock driver for Kendryte K210 platforms.
......
This diff is collapsed.
......@@ -12,17 +12,41 @@
#include <serial.h>
#include <asm/io.h>
#include <dt-bindings/clock/k210-sysctl.h>
#include <dt-bindings/mfd/k210-sysctl.h>
#include <kendryte/pll.h>
#include <linux/bitfield.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#define CLK_K210_PLL "k210_clk_pll"
/**
* struct k210_pll_params - K210 PLL parameters
* @off: The offset of the PLL from the base sysctl address
* @shift: The offset of the LSB of the lock status
* @width: The number of bits in the lock status
*/
struct k210_pll_params {
u8 off;
u8 shift;
u8 width;
};
static const struct k210_pll_params k210_plls[] = {
#define PLL(_off, _shift, _width) { \
.off = (_off), \
.shift = (_shift), \
.width = (_width), \
}
[0] = PLL(K210_SYSCTL_PLL0, 0, 2),
[1] = PLL(K210_SYSCTL_PLL1, 8, 1),
[2] = PLL(K210_SYSCTL_PLL2, 16, 1),
#undef PLL
};
#ifdef CONFIG_CLK_K210_SET_RATE
static int k210_pll_enable(struct clk *clk);
static int k210_pll_disable(struct clk *clk);
int k210_pll_enable(struct k210_clk_priv *priv, int id);
int k210_pll_disable(struct k210_clk_priv *priv, int id);
ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, ulong rate_in);
/*
* The PLL included with the Kendryte K210 appears to be a True Circuits, Inc.
......@@ -423,12 +447,12 @@ TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in,
return 0;
}
static ulong k210_pll_set_rate(struct clk *clk, ulong rate)
static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate,
ulong rate_in)
{
int err;
long long rate_in = clk_get_parent_rate(clk);
const struct k210_pll_params *pll = &k210_plls[id];
struct k210_pll_config config = {};
struct k210_pll *pll = to_k210_pll(clk);
u32 reg;
if (rate_in < 0)
......@@ -447,7 +471,7 @@ static ulong k210_pll_set_rate(struct clk *clk, ulong rate)
*/
k210_pll_disable(clk);
reg = readl(pll->reg);
reg = readl(priv->base + pll->off);
reg &= ~K210_PLL_CLKR
& ~K210_PLL_CLKF
& ~K210_PLL_CLKOD
......@@ -456,7 +480,7 @@ static ulong k210_pll_set_rate(struct clk *clk, ulong rate)
| FIELD_PREP(K210_PLL_CLKF, config.f - 1)
| FIELD_PREP(K210_PLL_CLKOD, config.od - 1)
| FIELD_PREP(K210_PLL_BWADJ, config.f - 1);
writel(reg, pll->reg);
writel(reg, priv->base + pll->off);
err = k210_pll_enable(clk);
if (err)
......@@ -465,14 +489,18 @@ static ulong k210_pll_set_rate(struct clk *clk, ulong rate)
serial_setbrg();
return clk_get_rate(clk);
}
#else
ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate,
ulong rate_in)
{
return -ENOSYS;
}
#endif /* CONFIG_CLK_K210_SET_RATE */
static ulong k210_pll_get_rate(struct clk *clk)
ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, ulong rate_in)
{
long long rate_in = clk_get_parent_rate(clk);
struct k210_pll *pll = to_k210_pll(clk);
u64 r, f, od;
u32 reg = readl(pll->reg);
u32 reg = readl(priv->base + k210_plls[id].off);
if (rate_in < 0 || (reg & K210_PLL_BYPASS))
return rate_in;
......@@ -491,57 +519,58 @@ static ulong k210_pll_get_rate(struct clk *clk)
* Wait for the PLL to be locked. If the PLL is not locked, try clearing the
* slip before retrying
*/
static void k210_pll_waitfor_lock(struct k210_pll *pll)
void k210_pll_waitfor_lock(struct k210_clk_priv *priv, int id)
{
const struct k210_pll_params *pll = &k210_plls[id];
u32 mask = GENMASK(pll->width - 1, 0) << pll->shift;
while (true) {
u32 reg = readl(pll->lock);
u32 reg = readl(priv->base + K210_SYSCTL_PLL_LOCK);
if ((reg & mask) == mask)
break;
reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP);
writel(reg, pll->lock);
writel(reg, priv->base + K210_SYSCTL_PLL_LOCK);
}
}
/* Adapted from sysctl_pll_enable */
static int k210_pll_enable(struct clk *clk)
int k210_pll_enable(struct k210_clk_priv *priv, int id)
{
struct k210_pll *pll = to_k210_pll(clk);
u32 reg = readl(pll->reg);
const struct k210_pll_params *pll = &k210_plls[id];
u32 reg = readl(priv->base + pll->off);
if ((reg & K210_PLL_PWRD) && (reg & K210_PLL_EN) &&
!(reg & K210_PLL_RESET))
return 0;
reg |= K210_PLL_PWRD;
writel(reg, pll->reg);
writel(reg, priv->base + pll->off);
/* Ensure reset is low before asserting it */
reg &= ~K210_PLL_RESET;
writel(reg, pll->reg);
writel(reg, priv->base + pll->off);
reg |= K210_PLL_RESET;
writel(reg, pll->reg);
writel(reg, priv->base + pll->off);
nop();
nop();
reg &= ~K210_PLL_RESET;
writel(reg, pll->reg);
writel(reg, priv->base + pll->off);
k210_pll_waitfor_lock(pll);
k210_pll_waitfor_lock(priv, id);
reg &= ~K210_PLL_BYPASS;
reg |= K210_PLL_EN;
writel(reg, pll->reg);
writel(reg, priv->base + pll->off);
return 0;
}
static int k210_pll_disable(struct clk *clk)
int k210_pll_disable(struct k210_clk_priv *priv, int id)
{
struct k210_pll *pll = to_k210_pll(clk);
u32 reg = readl(pll->reg);
const struct k210_pll_params *pll = &k210_plls[id];
u32 reg = readl(priv->base + pll->off);
/*
* Bypassing before powering off is important so child clocks don't stop
......@@ -549,37 +578,10 @@ static int k210_pll_disable(struct clk *clk)
* of the cpu clock.
*/
reg |= K210_PLL_BYPASS;
writel(reg, pll->reg);
writel(reg, priv->base + pll->off);
reg &= ~K210_PLL_PWRD;
reg &= ~K210_PLL_EN;
writel(reg, pll->reg);
writel(reg, priv->base + pll->off);
return 0;
}
const struct clk_ops k210_pll_ops = {
.get_rate = k210_pll_get_rate,
#ifdef CONFIG_CLK_K210_SET_RATE
.set_rate = k210_pll_set_rate,
#endif
.enable = k210_pll_enable,
.disable = k210_pll_disable,
};
struct clk *k210_register_pll_struct(const char *name, const char *parent_name,
struct k210_pll *pll)
{
int ret;
struct clk *clk = &pll->clk;
ret = clk_register(clk, CLK_K210_PLL, name, parent_name);
if (ret)
return ERR_PTR(ret);
return clk;
}
U_BOOT_DRIVER(k210_pll) = {
.name = CLK_K210_PLL,
.id = UCLASS_CLK,
.ops = &k210_pll_ops,
};
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
* Copyright (C) 2019-21 Sean Anderson <seanga2@gmail.com>
*/
#ifndef CLOCK_K210_SYSCTL_H
......@@ -9,52 +9,50 @@
/*
* Arbitrary identifiers for clocks.
*/
#define K210_CLK_NONE 0
#define K210_CLK_IN0_H 1
#define K210_CLK_PLL0_H 2
#define K210_CLK_PLL0 3
#define K210_CLK_PLL1 4
#define K210_CLK_PLL2 5
#define K210_CLK_PLL2_H 6
#define K210_CLK_CPU 7
#define K210_CLK_SRAM0 8
#define K210_CLK_SRAM1 9
#define K210_CLK_APB0 10
#define K210_CLK_APB1 11
#define K210_CLK_APB2 12
#define K210_CLK_ROM 13
#define K210_CLK_DMA 14
#define K210_CLK_AI 15
#define K210_CLK_DVP 16
#define K210_CLK_FFT 17
#define K210_CLK_GPIO 18
#define K210_CLK_SPI0 19
#define K210_CLK_SPI1 20
#define K210_CLK_SPI2 21
#define K210_CLK_SPI3 22
#define K210_CLK_I2S0 23
#define K210_CLK_I2S1 24
#define K210_CLK_I2S2 25
#define K210_CLK_I2S0_M 26
#define K210_CLK_I2S1_M 27
#define K210_CLK_I2S2_M 28
#define K210_CLK_I2C0 29
#define K210_CLK_I2C1 30
#define K210_CLK_I2C2 31
#define K210_CLK_UART1 32
#define K210_CLK_UART2 33
#define K210_CLK_UART3 34
#define K210_CLK_AES 35
#define K210_CLK_FPIOA 36
#define K210_CLK_TIMER0 37
#define K210_CLK_TIMER1 38
#define K210_CLK_TIMER2 39
#define K210_CLK_WDT0 40
#define K210_CLK_WDT1 41
#define K210_CLK_SHA 42
#define K210_CLK_OTP 43
#define K210_CLK_RTC 44
#define K210_CLK_ACLK 45
#define K210_CLK_CLINT 46
#define K210_CLK_PLL0 0
#define K210_CLK_PLL1 1
#define K210_CLK_PLL2 2
#define K210_CLK_CPU 3
#define K210_CLK_SRAM0 4
#define K210_CLK_SRAM1 5
#define K210_CLK_ACLK 6
#define K210_CLK_CLINT 7
#define K210_CLK_APB0 8
#define K210_CLK_APB1 9
#define K210_CLK_APB2 10
#define K210_CLK_ROM 11
#define K210_CLK_DMA 12
#define K210_CLK_AI 13
#define K210_CLK_DVP 14
#define K210_CLK_FFT 15
#define K210_CLK_GPIO 16
#define K210_CLK_SPI0 17
#define K210_CLK_SPI1 18
#define K210_CLK_SPI2 19
#define K210_CLK_SPI3 20
#define K210_CLK_I2S0 21
#define K210_CLK_I2S1 22
#define K210_CLK_I2S2 23
#define K210_CLK_I2S0_M 24
#define K210_CLK_I2S1_M 25
#define K210_CLK_I2S2_M 26
#define K210_CLK_I2C0 27
#define K210_CLK_I2C1 28
#define K210_CLK_I2C2 29
#define K210_CLK_UART1 30
#define K210_CLK_UART2 31
#define K210_CLK_UART3 32
#define K210_CLK_AES 33
#define K210_CLK_FPIOA 34
#define K210_CLK_TIMER0 35
#define K210_CLK_TIMER1 36
#define K210_CLK_TIMER2 37
#define K210_CLK_WDT0 38
#define K210_CLK_WDT1 39
#define K210_CLK_SHA 40
#define K210_CLK_OTP 41
#define K210_CLK_RTC 42
#define K210_CLK_IN0 43
#endif /* CLOCK_K210_SYSCTL_H */
......@@ -25,16 +25,6 @@
#define K210_PLL_CLEAR_SLIP 2
#define K210_PLL_TEST_OUT 3
struct k210_pll {
struct clk clk;
void __iomem *reg; /* Base PLL register */
void __iomem *lock; /* Common PLL lock register */
u8 shift; /* Offset of bits in lock register */
u8 width; /* Width of lock bits to test against */
};
#define to_k210_pll(_clk) container_of(_clk, struct k210_pll, clk)
struct k210_pll_config {
u8 r;
u8 f;
......@@ -51,8 +41,18 @@ TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in,
#endif
extern const struct clk_ops k210_pll_ops;
/**
* struct k210_clk_priv - K210 clock driver private data
* @base: The base address of the sysctl device
* @in0: The "in0" external oscillator
*/
struct k210_clk_priv {
void __iomem *base;
struct clk in0;
};
struct clk *k210_register_pll_struct(const char *name, const char *parent_name,
struct k210_pll *pll);
ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, ulong rate_in);
ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, ulong rate_in);
int k210_pll_enable(struct k210_clk_priv *priv, int id);
int k210_pll_disable(struct k210_clk_priv *priv, int id);
#endif /* K210_PLL_H */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment