summaryrefslogtreecommitdiff
path: root/drivers/clk/kendryte/pll.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/kendryte/pll.c')
-rw-r--r--drivers/clk/kendryte/pll.c114
1 files changed, 58 insertions, 56 deletions
diff --git a/drivers/clk/kendryte/pll.c b/drivers/clk/kendryte/pll.c
index 184f37aaf2..1f0275c83f 100644
--- a/drivers/clk/kendryte/pll.c
+++ b/drivers/clk/kendryte/pll.c
@@ -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,
-};