summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig1
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-composite.c64
-rw-r--r--drivers/clk/clk-divider.c6
-rw-r--r--drivers/clk/clk-fixed-factor.c3
-rw-r--r--drivers/clk/clk-gate.c6
-rw-r--r--drivers/clk/clk-mux.c12
-rw-r--r--drivers/clk/clk-uclass.c8
-rw-r--r--drivers/clk/clk_stm32mp1.c295
-rw-r--r--drivers/clk/clk_versal.c8
-rw-r--r--drivers/clk/imx/clk-gate2.c4
-rw-r--r--drivers/clk/imx/clk-imx8mp.c2
-rw-r--r--drivers/clk/kendryte/Kconfig12
-rw-r--r--drivers/clk/kendryte/Makefile1
-rw-r--r--drivers/clk/kendryte/bypass.c270
-rw-r--r--drivers/clk/kendryte/clk.c663
-rw-r--r--drivers/clk/kendryte/pll.c601
-rw-r--r--drivers/clk/owl/clk_owl.c9
-rw-r--r--drivers/clk/owl/clk_owl.h2
19 files changed, 1884 insertions, 84 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 8b8b719999..82cb1874e1 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -156,6 +156,7 @@ source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/at91/Kconfig"
source "drivers/clk/exynos/Kconfig"
source "drivers/clk/imx/Kconfig"
+source "drivers/clk/kendryte/Kconfig"
source "drivers/clk/meson/Kconfig"
source "drivers/clk/mvebu/Kconfig"
source "drivers/clk/owl/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e01783391d..d911954581 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
obj-$(CONFIG_CLK_EXYNOS) += exynos/
obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
+obj-$(CONFIG_CLK_K210) += kendryte/
obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
obj-$(CONFIG_CLK_OWL) += owl/
obj-$(CONFIG_CLK_RENESAS) += renesas/
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 414185031e..819bfca2fc 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -24,7 +24,10 @@ static u8 clk_composite_get_parent(struct clk *clk)
(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
struct clk *mux = composite->mux;
- return clk_mux_get_parent(mux);
+ if (mux)
+ return clk_mux_get_parent(mux);
+ else
+ return 0;
}
static int clk_composite_set_parent(struct clk *clk, struct clk *parent)
@@ -34,7 +37,10 @@ static int clk_composite_set_parent(struct clk *clk, struct clk *parent)
const struct clk_ops *mux_ops = composite->mux_ops;
struct clk *mux = composite->mux;
- return mux_ops->set_parent(mux, parent);
+ if (mux && mux_ops)
+ return mux_ops->set_parent(mux, parent);
+ else
+ return -ENOTSUPP;
}
static unsigned long clk_composite_recalc_rate(struct clk *clk)
@@ -44,7 +50,10 @@ static unsigned long clk_composite_recalc_rate(struct clk *clk)
const struct clk_ops *rate_ops = composite->rate_ops;
struct clk *rate = composite->rate;
- return rate_ops->get_rate(rate);
+ if (rate && rate_ops)
+ return rate_ops->get_rate(rate);
+ else
+ return clk_get_parent_rate(clk);
}
static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate)
@@ -54,7 +63,10 @@ static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate)
const struct clk_ops *rate_ops = composite->rate_ops;
struct clk *clk_rate = composite->rate;
- return rate_ops->set_rate(clk_rate, rate);
+ if (rate && rate_ops)
+ return rate_ops->set_rate(clk_rate, rate);
+ else
+ return clk_get_rate(clk);
}
static int clk_composite_enable(struct clk *clk)
@@ -64,7 +76,10 @@ static int clk_composite_enable(struct clk *clk)
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk *gate = composite->gate;
- return gate_ops->enable(gate);
+ if (gate && gate_ops)
+ return gate_ops->enable(gate);
+ else
+ return 0;
}
static int clk_composite_disable(struct clk *clk)
@@ -74,15 +89,12 @@ static int clk_composite_disable(struct clk *clk)
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk *gate = composite->gate;
- gate_ops->disable(gate);
-
- return 0;
+ if (gate && gate_ops)
+ return gate_ops->disable(gate);
+ else
+ return 0;
}
-struct clk_ops clk_composite_ops = {
- /* This will be set according to clk_register_composite */
-};
-
struct clk *clk_register_composite(struct device *dev, const char *name,
const char * const *parent_names,
int num_parents, struct clk *mux,
@@ -96,7 +108,9 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
struct clk *clk;
struct clk_composite *composite;
int ret;
- struct clk_ops *composite_ops = &clk_composite_ops;
+
+ if (!num_parents || (num_parents != 1 && !mux))
+ return ERR_PTR(-EINVAL);
composite = kzalloc(sizeof(*composite), GFP_KERNEL);
if (!composite)
@@ -105,8 +119,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
if (mux && mux_ops) {
composite->mux = mux;
composite->mux_ops = mux_ops;
- if (mux_ops->set_parent)
- composite_ops->set_parent = clk_composite_set_parent;
mux->data = (ulong)composite;
}
@@ -115,11 +127,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
clk = ERR_PTR(-EINVAL);
goto err;
}
- composite_ops->get_rate = clk_composite_recalc_rate;
-
- /* .set_rate requires either .round_rate or .determine_rate */
- if (rate_ops->set_rate)
- composite_ops->set_rate = clk_composite_set_rate;
composite->rate = rate;
composite->rate_ops = rate_ops;
@@ -134,8 +141,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
composite->gate = gate;
composite->gate_ops = gate_ops;
- composite_ops->enable = clk_composite_enable;
- composite_ops->disable = clk_composite_disable;
gate->data = (ulong)composite;
}
@@ -147,6 +152,13 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
goto err;
}
+ if (composite->mux)
+ composite->mux->dev = clk->dev;
+ if (composite->rate)
+ composite->rate->dev = clk->dev;
+ if (composite->gate)
+ composite->gate->dev = clk->dev;
+
return clk;
err:
@@ -154,6 +166,14 @@ err:
return clk;
}
+static const struct clk_ops clk_composite_ops = {
+ .set_parent = clk_composite_set_parent,
+ .get_rate = clk_composite_recalc_rate,
+ .set_rate = clk_composite_set_rate,
+ .enable = clk_composite_enable,
+ .disable = clk_composite_disable,
+};
+
U_BOOT_DRIVER(clk_composite) = {
.name = UBOOT_DM_CLK_COMPOSITE,
.id = UCLASS_CLK,
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 2a68719eb6..34658536c4 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -73,8 +73,7 @@ unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
static ulong clk_divider_recalc_rate(struct clk *clk)
{
- struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_divider *divider = to_clk_divider(clk);
unsigned long parent_rate = clk_get_parent_rate(clk);
unsigned int val;
@@ -153,8 +152,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
static ulong clk_divider_set_rate(struct clk *clk, unsigned long rate)
{
- struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_divider *divider = to_clk_divider(clk);
unsigned long parent_rate = clk_get_parent_rate(clk);
int value;
u32 val;
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index 2ceb6bb171..0eb24b87fc 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -20,8 +20,7 @@
static ulong clk_factor_recalc_rate(struct clk *clk)
{
- struct clk_fixed_factor *fix =
- to_clk_fixed_factor(dev_get_clk_ptr(clk->dev));
+ struct clk_fixed_factor *fix = to_clk_fixed_factor(clk);
unsigned long parent_rate = clk_get_parent_rate(clk);
unsigned long long int rate;
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 23c1f2c4ba..98e4b80b32 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -46,8 +46,7 @@
*/
static void clk_gate_endisable(struct clk *clk, int enable)
{
- struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_gate *gate = to_clk_gate(clk);
int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
u32 reg;
@@ -89,8 +88,7 @@ static int clk_gate_disable(struct clk *clk)
int clk_gate_is_enabled(struct clk *clk)
{
- struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_gate *gate = to_clk_gate(clk);
u32 reg;
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index c69cce0565..26991a5bc8 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -38,8 +38,7 @@
int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
unsigned int val)
{
- struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_mux *mux = to_clk_mux(clk);
int num_parents = mux->num_parents;
if (table) {
@@ -82,8 +81,7 @@ unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
u8 clk_mux_get_parent(struct clk *clk)
{
- struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_mux *mux = to_clk_mux(clk);
u32 val;
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
@@ -100,8 +98,7 @@ u8 clk_mux_get_parent(struct clk *clk)
static int clk_fetch_parent_index(struct clk *clk,
struct clk *parent)
{
- struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_mux *mux = to_clk_mux(clk);
int i;
@@ -118,8 +115,7 @@ static int clk_fetch_parent_index(struct clk *clk,
static int clk_mux_set_parent(struct clk *clk, struct clk *parent)
{
- struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_mux *mux = to_clk_mux(clk);
int index;
u32 val;
u32 reg;
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 9ffc2243cb..70df9d410f 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -123,7 +123,7 @@ static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name,
return clk_get_by_index_tail(ret, dev_ofnode(dev), &args, "clocks",
- index > 0, clk);
+ index, clk);
}
int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
@@ -135,7 +135,7 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
index, &args);
return clk_get_by_index_tail(ret, dev_ofnode(dev), &args, "clocks",
- index > 0, clk);
+ index, clk);
}
int clk_get_by_index_nodev(ofnode node, int index, struct clk *clk)
@@ -144,10 +144,10 @@ int clk_get_by_index_nodev(ofnode node, int index, struct clk *clk)
int ret;
ret = ofnode_parse_phandle_with_args(node, "clocks", "#clock-cells", 0,
- index > 0, &args);
+ index, &args);
return clk_get_by_index_tail(ret, node, &args, "clocks",
- index > 0, clk);
+ index, clk);
}
int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk)
diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c
index 6c5eddbd9a..c8840b9e5f 100644
--- a/drivers/clk/clk_stm32mp1.c
+++ b/drivers/clk/clk_stm32mp1.c
@@ -17,6 +17,7 @@
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <asm/arch/sys_proto.h>
#include <dt-bindings/clock/stm32mp1-clks.h>
#include <dt-bindings/clock/stm32mp1-clksrc.h>
@@ -644,8 +645,18 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = {
};
#ifdef STM32MP1_CLOCK_TREE_INIT
+
/* define characteristic of PLL according type */
+#define DIVM_MIN 0
+#define DIVM_MAX 63
#define DIVN_MIN 24
+#define DIVP_MIN 0
+#define DIVP_MAX 127
+#define FRAC_MAX 8192
+
+#define PLL1600_VCO_MIN 800000000
+#define PLL1600_VCO_MAX 1600000000
+
static const struct stm32mp1_pll stm32mp1_pll[PLL_TYPE_NB] = {
[PLL_800] = {
.refclk_min = 4,
@@ -1190,6 +1201,213 @@ static ulong stm32mp1_clk_get_rate(struct clk *clk)
}
#ifdef STM32MP1_CLOCK_TREE_INIT
+
+bool stm32mp1_supports_opp(u32 opp_id, u32 cpu_type)
+{
+ unsigned int id;
+
+ switch (opp_id) {
+ case 1:
+ case 2:
+ id = opp_id;
+ break;
+ default:
+ id = 1; /* default value */
+ break;
+ }
+
+ switch (cpu_type) {
+ case CPU_STM32MP157Fxx:
+ case CPU_STM32MP157Dxx:
+ case CPU_STM32MP153Fxx:
+ case CPU_STM32MP153Dxx:
+ case CPU_STM32MP151Fxx:
+ case CPU_STM32MP151Dxx:
+ return true;
+ default:
+ return id == 1;
+ }
+}
+
+__weak void board_vddcore_init(u32 voltage_mv)
+{
+}
+
+/*
+ * gets OPP parameters (frequency in KHz and voltage in mV) from
+ * an OPP table subnode. Platform HW support capabilities are also checked.
+ * Returns 0 on success and a negative FDT error code on failure.
+ */
+static int stm32mp1_get_opp(u32 cpu_type, ofnode subnode,
+ u32 *freq_khz, u32 *voltage_mv)
+{
+ u32 opp_hw;
+ u64 read_freq_64;
+ u32 read_voltage_32;
+
+ *freq_khz = 0;
+ *voltage_mv = 0;
+
+ opp_hw = ofnode_read_u32_default(subnode, "opp-supported-hw", 0);
+ if (opp_hw)
+ if (!stm32mp1_supports_opp(opp_hw, cpu_type))
+ return -FDT_ERR_BADVALUE;
+
+ read_freq_64 = ofnode_read_u64_default(subnode, "opp-hz", 0) /
+ 1000ULL;
+ read_voltage_32 = ofnode_read_u32_default(subnode, "opp-microvolt", 0) /
+ 1000U;
+
+ if (!read_voltage_32 || !read_freq_64)
+ return -FDT_ERR_NOTFOUND;
+
+ /* Frequency value expressed in KHz must fit on 32 bits */
+ if (read_freq_64 > U32_MAX)
+ return -FDT_ERR_BADVALUE;
+
+ /* Millivolt value must fit on 16 bits */
+ if (read_voltage_32 > U16_MAX)
+ return -FDT_ERR_BADVALUE;
+
+ *freq_khz = (u32)read_freq_64;
+ *voltage_mv = read_voltage_32;
+
+ return 0;
+}
+
+/*
+ * parses OPP table in DT and finds the parameters for the
+ * highest frequency supported by the HW platform.
+ * Returns 0 on success and a negative FDT error code on failure.
+ */
+int stm32mp1_get_max_opp_freq(struct stm32mp1_clk_priv *priv, u64 *freq_hz)
+{
+ ofnode node, subnode;
+ int ret;
+ u32 freq = 0U, voltage = 0U;
+ u32 cpu_type = get_cpu_type();
+
+ node = ofnode_by_compatible(ofnode_null(), "operating-points-v2");
+ if (!ofnode_valid(node))
+ return -FDT_ERR_NOTFOUND;
+
+ ofnode_for_each_subnode(subnode, node) {
+ unsigned int read_freq;
+ unsigned int read_voltage;
+
+ ret = stm32mp1_get_opp(cpu_type, subnode,
+ &read_freq, &read_voltage);
+ if (ret)
+ continue;
+
+ if (read_freq > freq) {
+ freq = read_freq;
+ voltage = read_voltage;
+ }
+ }
+
+ if (!freq || !voltage)
+ return -FDT_ERR_NOTFOUND;
+
+ *freq_hz = (u64)1000U * freq;
+ board_vddcore_init(voltage);
+
+ return 0;
+}
+
+static int stm32mp1_pll1_opp(struct stm32mp1_clk_priv *priv, int clksrc,
+ u32 *pllcfg, u32 *fracv)
+{
+ u32 post_divm;
+ u32 input_freq;
+ u64 output_freq;
+ u64 freq;
+ u64 vco;
+ u32 divm, divn, divp, frac;
+ int i, ret;
+ u32 diff;
+ u32 best_diff = U32_MAX;
+
+ /* PLL1 is 1600 */
+ const u32 DIVN_MAX = stm32mp1_pll[PLL_1600].divn_max;
+ const u32 POST_DIVM_MIN = stm32mp1_pll[PLL_1600].refclk_min * 1000000U;
+ const u32 POST_DIVM_MAX = stm32mp1_pll[PLL_1600].refclk_max * 1000000U;
+
+ ret = stm32mp1_get_max_opp_freq(priv, &output_freq);
+ if (ret) {
+ debug("PLL1 OPP configuration not found (%d).\n", ret);
+ return ret;
+ }
+
+ switch (clksrc) {
+ case CLK_PLL12_HSI:
+ input_freq = stm32mp1_clk_get_fixed(priv, _HSI);
+ break;
+ case CLK_PLL12_HSE:
+ input_freq = stm32mp1_clk_get_fixed(priv, _HSE);
+ break;
+ default:
+ return -EINTR;
+ }
+
+ /* Following parameters have always the same value */
+ pllcfg[PLLCFG_Q] = 0;
+ pllcfg[PLLCFG_R] = 0;
+ pllcfg[PLLCFG_O] = PQR(1, 0, 0);
+
+ for (divm = DIVM_MAX; divm >= DIVM_MIN; divm--) {
+ post_divm = (u32)(input_freq / (divm + 1));
+ if (post_divm < POST_DIVM_MIN || post_divm > POST_DIVM_MAX)
+ continue;
+
+ for (divp = DIVP_MIN; divp <= DIVP_MAX; divp++) {
+ freq = output_freq * (divm + 1) * (divp + 1);
+ divn = (u32)((freq / input_freq) - 1);
+ if (divn < DIVN_MIN || divn > DIVN_MAX)
+ continue;
+
+ frac = (u32)(((freq * FRAC_MAX) / input_freq) -
+ ((divn + 1) * FRAC_MAX));
+ /* 2 loops to refine the fractional part */
+ for (i = 2; i != 0; i--) {
+ if (frac > FRAC_MAX)
+ break;
+
+ vco = (post_divm * (divn + 1)) +
+ ((post_divm * (u64)frac) /
+ FRAC_MAX);
+ if (vco < (PLL1600_VCO_MIN / 2) ||
+ vco > (PLL1600_VCO_MAX / 2)) {
+ frac++;
+ continue;
+ }
+ freq = vco / (divp + 1);
+ if (output_freq < freq)
+ diff = (u32)(freq - output_freq);
+ else
+ diff = (u32)(output_freq - freq);
+ if (diff < best_diff) {
+ pllcfg[PLLCFG_M] = divm;
+ pllcfg[PLLCFG_N] = divn;
+ pllcfg[PLLCFG_P] = divp;
+ *fracv = frac;
+
+ if (diff == 0)
+ return 0;
+
+ best_diff = diff;
+ }
+ frac++;
+ }
+ }
+ }
+
+ if (best_diff == U32_MAX)
+ return -1;
+
+ return 0;
+}
+
static void stm32mp1_ls_osc_set(int enable, fdt_addr_t rcc, u32 offset,
u32 mask_on)
{
@@ -1661,9 +1879,12 @@ static int stm32mp1_clktree(struct udevice *dev)
unsigned int clksrc[CLKSRC_NB];
unsigned int clkdiv[CLKDIV_NB];
unsigned int pllcfg[_PLL_NB][PLLCFG_NB];
- ofnode plloff[_PLL_NB];
- int ret, len;
- uint i;
+ unsigned int pllfracv[_PLL_NB];
+ unsigned int pllcsg[_PLL_NB][PLLCSG_NB];
+ bool pllcfg_valid[_PLL_NB];
+ bool pllcsg_set[_PLL_NB];
+ int ret;
+ int i, len;
int lse_css = 0;
const u32 *pkcs_cell;
@@ -1683,16 +1904,43 @@ static int stm32mp1_clktree(struct udevice *dev)
/* check mandatory field in each pll */
for (i = 0; i < _PLL_NB; i++) {
char name[12];
+ ofnode node;
sprintf(name, "st,pll@%d", i);
- plloff[i] = dev_read_subnode(dev, name);
- if (!ofnode_valid(plloff[i]))
- continue;
- ret = ofnode_read_u32_array(plloff[i], "cfg",
- pllcfg[i], PLLCFG_NB);
- if (ret < 0) {
- debug("field cfg invalid: error %d\n", ret);
- return -FDT_ERR_NOTFOUND;
+ node = dev_read_subnode(dev, name);
+ pllcfg_valid[i] = ofnode_valid(node);
+ pllcsg_set[i] = false;
+ if (pllcfg_valid[i]) {
+ debug("DT for PLL %d @ %s\n", i, name);
+ ret = ofnode_read_u32_array(node, "cfg",
+ pllcfg[i], PLLCFG_NB);
+ if (ret < 0) {
+ debug("field cfg invalid: error %d\n", ret);
+ return -FDT_ERR_NOTFOUND;
+ }
+ pllfracv[i] = ofnode_read_u32_default(node, "frac", 0);
+
+ ret = ofnode_read_u32_array(node, "csg", pllcsg[i],
+ PLLCSG_NB);
+ if (!ret) {
+ pllcsg_set[i] = true;
+ } else if (ret != -FDT_ERR_NOTFOUND) {
+ debug("invalid csg node for pll@%d res=%d\n",
+ i, ret);
+ return ret;
+ }
+ } else if (i == _PLL1) {
+ /* use OPP for PLL1 for A7 CPU */
+ debug("DT for PLL %d with OPP\n", i);
+ ret = stm32mp1_pll1_opp(priv,
+ clksrc[CLKSRC_PLL12],
+ pllcfg[i],
+ &pllfracv[i]);
+ if (ret) {
+ debug("PLL %d with OPP error = %d\n", i, ret);
+ return ret;
+ }
+ pllcfg_valid[i] = true;
}
}
@@ -1778,29 +2026,18 @@ static int stm32mp1_clktree(struct udevice *dev)
/* configure and start PLLs */
debug("configure PLLs\n");
for (i = 0; i < _PLL_NB; i++) {
- u32 fracv;
- u32 csg[PLLCSG_NB];
-
- debug("configure PLL %d @ %d\n", i,
- ofnode_to_offset(plloff[i]));
- if (!ofnode_valid(plloff[i]))
+ if (!pllcfg_valid[i])
continue;
-
- fracv = ofnode_read_u32_default(plloff[i], "frac", 0);
- pll_config(priv, i, pllcfg[i], fracv);
- ret = ofnode_read_u32_array(plloff[i], "csg", csg, PLLCSG_NB);
- if (!ret) {
- pll_csg(priv, i, csg);
- } else if (ret != -FDT_ERR_NOTFOUND) {
- debug("invalid csg node for pll@%d res=%d\n", i, ret);
- return ret;
- }
+ debug("configure PLL %d\n", i);
+ pll_config(priv, i, pllcfg[i], pllfracv[i]);
+ if (pllcsg_set[i])
+ pll_csg(priv, i, pllcsg[i]);
pll_start(priv, i);
}
/* wait and start PLLs ouptut when ready */
for (i = 0; i < _PLL_NB; i++) {
- if (!ofnode_valid(plloff[i]))
+ if (!pllcfg_valid[i])
continue;
debug("output PLL %d\n", i);
pll_output(priv, i, pllcfg[i][PLLCFG_O]);
@@ -2050,6 +2287,8 @@ static int stm32mp1_clk_probe(struct udevice *dev)
/* clock tree init is done only one time, before relocation */
if (!(gd->flags & GD_FLG_RELOC))
result = stm32mp1_clktree(dev);
+ if (result)
+ printf("clock tree initialization failed (%d)\n", result);
#endif
#ifndef CONFIG_SPL_BUILD
diff --git a/drivers/clk/clk_versal.c b/drivers/clk/clk_versal.c
index 2fb3171d71..6f82b60f04 100644
--- a/drivers/clk/clk_versal.c
+++ b/drivers/clk/clk_versal.c
@@ -117,7 +117,6 @@ struct versal_clk_priv {
struct versal_clock *clk;
};
-static ulong alt_ref_clk;
static ulong pl_alt_ref_clk;
static ulong ref_clk;
@@ -548,8 +547,7 @@ int soc_clk_dump(void)
printf("\n ****** VERSAL CLOCKS *****\n");
- printf("alt_ref_clk:%ld pl_alt_ref_clk:%ld ref_clk:%ld\n",
- alt_ref_clk, pl_alt_ref_clk, ref_clk);
+ printf("pl_alt_ref_clk:%ld ref_clk:%ld\n", pl_alt_ref_clk, ref_clk);
for (i = 0; i < clock_max_idx; i++) {
debug("%s\n", clock[i].clk_name);
ret = versal_get_clock_type(i, &type);
@@ -667,10 +665,6 @@ static int versal_clk_probe(struct udevice *dev)
debug("%s\n", __func__);
- ret = versal_clock_get_freq_by_name("alt_ref_clk", dev, &alt_ref_clk);
- if (ret < 0)
- return -EINVAL;
-
ret = versal_clock_get_freq_by_name("pl_alt_ref_clk",
dev, &pl_alt_ref_clk);
if (ret < 0)
diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c
index b38890d5ba..40b2d4caab 100644
--- a/drivers/clk/imx/clk-gate2.c
+++ b/drivers/clk/imx/clk-gate2.c
@@ -39,7 +39,7 @@ struct clk_gate2 {
static int clk_gate2_enable(struct clk *clk)
{
- struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+ struct clk_gate2 *gate = to_clk_gate2(clk);
u32 reg;
reg = readl(gate->reg);
@@ -52,7 +52,7 @@ static int clk_gate2_enable(struct clk *clk)
static int clk_gate2_disable(struct clk *clk)
{
- struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+ struct clk_gate2 *gate = to_clk_gate2(clk);
u32 reg;
reg = readl(gate->reg);
diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c
index 3d7aebb8e5..124138cf51 100644
--- a/drivers/clk/imx/clk-imx8mp.c
+++ b/drivers/clk/imx/clk-imx8mp.c
@@ -282,7 +282,7 @@ static int imx8mp_clk_probe(struct udevice *dev)
clk_dm(IMX8MP_SYS_PLL2_1000M, imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1));
base = dev_read_addr_ptr(dev);
- if (base == (void *)FDT_ADDR_T_NONE)
+ if (!base)
return -EINVAL;
clk_dm(IMX8MP_CLK_A53_SRC, imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels)));
diff --git a/drivers/clk/kendryte/Kconfig b/drivers/clk/kendryte/Kconfig
new file mode 100644
index 0000000000..073fca0781
--- /dev/null
+++ b/drivers/clk/kendryte/Kconfig
@@ -0,0 +1,12 @@
+config CLK_K210
+ bool "Clock support for Kendryte K210"
+ depends on CLK && CLK_CCF && CLK_COMPOSITE_CCF
+ help
+ This enables support clock driver for Kendryte K210 platforms.
+
+config CLK_K210_SET_RATE
+ bool "Enable setting the Kendryte K210 PLL rate"
+ depends on CLK_K210
+ help
+ Add functionality to calculate new rates for K210 PLLs. Enabling this
+ feature adds around 1K to U-Boot's final size.
diff --git a/drivers/clk/kendryte/Makefile b/drivers/clk/kendryte/Makefile
new file mode 100644
index 0000000000..6fb68253ae
--- /dev/null
+++ b/drivers/clk/kendryte/Makefile
@@ -0,0 +1 @@
+obj-y += bypass.o clk.o pll.o
diff --git a/drivers/clk/kendryte/bypass.c b/drivers/clk/kendryte/bypass.c
new file mode 100644
index 0000000000..d1fd28175b
--- /dev/null
+++ b/drivers/clk/kendryte/bypass.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#define LOG_CATEGORY UCLASS_CLK
+#include <kendryte/bypass.h>
+
+#include <clk-uclass.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <log.h>
+
+#define CLK_K210_BYPASS "k210_clk_bypass"
+
+/*
+ * This is a small driver to do a software bypass of a clock if hardware bypass
+ * is not working. I have tried to write this in a generic fashion, so that it
+ * could be potentially broken out of the kendryte code at some future date.
+ *
+ * Say you have the following clock configuration
+ *
+ * +---+ +---+
+ * |osc| |pll|
+ * +---+ +---+
+ * ^
+ * /|
+ * / |
+ * / |
+ * / |
+ * / |
+ * +---+ +---+
+ * |clk| |clk|
+ * +---+ +---+
+ *
+ * But the pll does not have a bypass, so when you configure the pll, the
+ * configuration needs to change to look like
+ *
+ * +---+ +---+
+ * |osc| |pll|
+ * +---+ +---+
+ * ^
+ * |\
+ * | \
+ * | \
+ * | \
+ * | \
+ * +---+ +---+
+ * |clk| |clk|
+ * +---+ +---+
+ *
+ * To set this up, create a bypass clock with bypassee=pll and alt=osc. When
+ * creating the child clocks, set their parent to the bypass clock. After
+ * creating all the children, call k210_bypass_setchildren().
+ */
+
+static int k210_bypass_dobypass(struct k210_bypass *bypass)
+{
+ int ret, i;
+
+ /*
+ * If we already have saved parents, then the children are already
+ * bypassed
+ */
+ if (bypass->child_count && bypass->saved_parents[0])
+ return 0;
+
+ for (i = 0; i < bypass->child_count; i++) {
+ struct clk *child = bypass->children[i];
+ struct clk *parent = clk_get_parent(child);
+
+ if (IS_ERR(parent)) {
+ for (; i; i--)
+ bypass->saved_parents[i] = NULL;
+ return PTR_ERR(parent);
+ }
+ bypass->saved_parents[i] = parent;
+ }
+
+ for (i = 0; i < bypass->child_count; i++) {
+ struct clk *child = bypass->children[i];
+
+ ret = clk_set_parent(child, bypass->alt);
+ if (ret) {
+ for (; i; i--)
+ clk_set_parent(bypass->children[i],
+ bypass->saved_parents[i]);
+ for (i = 0; i < bypass->child_count; i++)
+ bypass->saved_parents[i] = NULL;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int k210_bypass_unbypass(struct k210_bypass *bypass)
+{
+ int err, ret, i;
+
+ if (!bypass->child_count && !bypass->saved_parents[0]) {
+ log_warning("Cannot unbypass children; dobypass not called first\n");
+ return 0;
+ }
+
+ ret = 0;
+ for (i = 0; i < bypass->child_count; i++) {
+ err = clk_set_parent(bypass->children[i],
+ bypass->saved_parents[i]);
+ if (err)
+ ret = err;
+ bypass->saved_parents[i] = NULL;
+ }
+ return ret;
+}
+
+static ulong k210_bypass_get_rate(struct clk *clk)
+{
+ struct k210_bypass *bypass = to_k210_bypass(clk);
+ const struct clk_ops *ops = bypass->bypassee_ops;
+
+ if (ops->get_rate)
+ return ops->get_rate(bypass->bypassee);
+ else
+ return clk_get_parent_rate(bypass->bypassee);
+}
+
+static ulong k210_bypass_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret;
+ struct k210_bypass *bypass = to_k210_bypass(clk);
+ const struct clk_ops *ops = bypass->bypassee_ops;
+
+ /* Don't bother bypassing if we aren't going to set the rate */
+ if (!ops->set_rate)
+ return k210_bypass_get_rate(clk);
+
+ ret = k210_bypass_dobypass(bypass);
+ if (ret)
+ return ret;
+
+ ret = ops->set_rate(bypass->bypassee, rate);
+ if (ret < 0)
+ return ret;
+
+ return k210_bypass_unbypass(bypass);
+}
+
+static int k210_bypass_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct k210_bypass *bypass = to_k210_bypass(clk);
+ const struct clk_ops *ops = bypass->bypassee_ops;
+
+ if (ops->set_parent)
+ return ops->set_parent(bypass->bypassee, parent);
+ else
+ return -ENOTSUPP;
+}
+
+/*
+ * For these next two functions, do the bypassing even if there is no
+ * en-/-disable function, since the bypassing itself can be observed in between
+ * calls.
+ */
+static int k210_bypass_enable(struct clk *clk)
+{
+ int ret;
+ struct k210_bypass *bypass = to_k210_bypass(clk);
+ const struct clk_ops *ops = bypass->bypassee_ops;
+
+ ret = k210_bypass_dobypass(bypass);
+ if (ret)
+ return ret;
+
+ if (ops->enable)
+ ret = ops->enable(bypass->bypassee);
+ else
+ ret = 0;
+ if (ret)
+ return ret;
+
+ return k210_bypass_unbypass(bypass);
+}
+
+static int k210_bypass_disable(struct clk *clk)
+{
+ int ret;
+ struct k210_bypass *bypass = to_k210_bypass(clk);
+ const struct clk_ops *ops = bypass->bypassee_ops;
+
+ ret = k210_bypass_dobypass(bypass);
+ if (ret)
+ return ret;
+
+ if (ops->disable)
+ return ops->disable(bypass->bypassee);
+ else
+ return 0;
+}
+
+static const struct clk_ops k210_bypass_ops = {
+ .get_rate = k210_bypass_get_rate,
+ .set_rate = k210_bypass_set_rate,
+ .set_parent = k210_bypass_set_parent,
+ .enable = k210_bypass_enable,
+ .disable = k210_bypass_disable,
+};
+
+int k210_bypass_set_children(struct clk *clk, struct clk **children,
+ size_t child_count)
+{
+ struct k210_bypass *bypass = to_k210_bypass(clk);
+
+ kfree(bypass->saved_parents);
+ if (child_count) {
+ bypass->saved_parents =
+ kcalloc(child_count, sizeof(struct clk *), GFP_KERNEL);
+ if (!bypass->saved_parents)
+ return -ENOMEM;
+ }
+ bypass->child_count = child_count;
+ bypass->children = children;
+
+ return 0;
+}
+
+struct clk *k210_register_bypass_struct(const char *name,
+ const char *parent_name,
+ struct k210_bypass *bypass)
+{
+ int ret;
+ struct clk *clk;
+
+ clk = &bypass->clk;
+
+ ret = clk_register(clk, CLK_K210_BYPASS, name, parent_name);
+ if (ret)
+ return ERR_PTR(ret);
+
+ bypass->bypassee->dev = clk->dev;
+ return clk;
+}
+
+struct clk *k210_register_bypass(const char *name, const char *parent_name,
+ struct clk *bypassee,
+ const struct clk_ops *bypassee_ops,
+ struct clk *alt)
+{
+ struct clk *clk;
+ struct k210_bypass *bypass;
+
+ bypass = kzalloc(sizeof(*bypass), GFP_KERNEL);
+ if (!bypass)
+ return ERR_PTR(-ENOMEM);
+
+ bypass->bypassee = bypassee;
+ bypass->bypassee_ops = bypassee_ops;
+ bypass->alt = alt;
+
+ clk = k210_register_bypass_struct(name, parent_name, bypass);
+ if (IS_ERR(clk))
+ kfree(bypass);
+ return clk;
+}
+
+U_BOOT_DRIVER(k210_bypass) = {
+ .name = CLK_K210_BYPASS,
+ .id = UCLASS_CLK,
+ .ops = &k210_bypass_ops,
+};
diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c
new file mode 100644
index 0000000000..981b3b7699
--- /dev/null
+++ b/drivers/clk/kendryte/clk.c
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+#include <kendryte/clk.h>
+
+#include <asm/io.h>
+#include <dt-bindings/clock/k210-sysctl.h>
+#include <dt-bindings/mfd/k210-sysctl.h>
+#include <dm.h>
+#include <log.h>
+#include <mapmem.h>
+
+#include <kendryte/bypass.h>
+#include <kendryte/pll.h>
+
+/* All methods are delegated to CCF clocks */
+
+static ulong k210_clk_get_rate(struct clk *clk)
+{
+ struct clk *c;
+ int err = clk_get_by_id(clk->id, &c);
+
+ if (err)
+ return err;
+ return clk_get_rate(c);
+}
+
+static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct clk *c;
+ int err = clk_get_by_id(clk->id, &c);
+
+ if (err)
+ return err;
+ return clk_set_rate(c, rate);
+}
+
+static int k210_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk *c, *p;
+ int err = clk_get_by_id(clk->id, &c);
+
+ if (err)
+ return err;
+
+ err = clk_get_by_id(parent->id, &p);
+ if (err)
+ return err;
+
+ return clk_set_parent(c, p);
+}
+
+static int k210_clk_endisable(struct clk *clk, bool enable)
+{
+ struct clk *c;
+ int err = clk_get_by_id(clk->id, &c);
+
+ if (err)
+ return err;
+ return enable ? clk_enable(c) : clk_disable(c);
+}
+
+static int k210_clk_enable(struct clk *clk)
+{
+ return k210_clk_endisable(clk, true);
+}
+
+static int k210_clk_disable(struct clk *clk)
+{
+ return k210_clk_endisable(clk, false);
+}
+
+static const struct clk_ops k210_clk_ops = {
+ .set_rate = k210_clk_set_rate,
+ .get_rate = k210_clk_get_rate,
+ .set_parent = k210_clk_set_parent,
+ .enable = k210_clk_enable,
+ .disable = k210_clk_disable,
+};
+
+/* Parents for muxed clocks */
+static const char * const generic_sels[] = { "in0_half", "pll0_half" };
+/* The first clock is in0, which is filled in by k210_clk_probe */
+static const char *aclk_sels[] = { NULL, "pll0_half" };
+static const char *pll2_sels[] = { NULL, "pll0", "pll1" };
+
+/*
+ * All parameters for different sub-clocks are collected into parameter arrays.
+ * These parameters are then initialized by the clock which uses them during
+ * probe. To save space, ids are automatically generated for each sub-clock by
+ * using an enum. Instead of storing a parameter struct for each clock, even for
+ * those clocks which don't use a particular type of sub-clock, we can just
+ * store the parameters for the clocks which need them.
+ *
+ * So why do it like this? Arranging all the sub-clocks together makes it very
+ * easy to find bugs in the code.
+ */
+
+#define DIV(id, off, shift, width) DIV_FLAGS(id, off, shift, width, 0)
+#define DIV_LIST \
+ DIV_FLAGS(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, \
+ CLK_DIVIDER_POWER_OF_TWO) \
+ DIV(K210_CLK_APB0, K210_SYSCTL_SEL0, 3, 3) \
+ DIV(K210_CLK_APB1, K210_SYSCTL_SEL0, 6, 3) \
+ DIV(K210_CLK_APB2, K210_SYSCTL_SEL0, 9, 3) \
+ DIV(K210_CLK_SRAM0, K210_SYSCTL_THR0, 0, 4) \
+ DIV(K210_CLK_SRAM1, K210_SYSCTL_THR0, 4, 4) \
+ DIV(K210_CLK_AI, K210_SYSCTL_THR0, 8, 4) \
+ DIV(K210_CLK_DVP, K210_SYSCTL_THR0, 12, 4) \
+ DIV(K210_CLK_ROM, K210_SYSCTL_THR0, 16, 4) \
+ DIV(K210_CLK_SPI0, K210_SYSCTL_THR1, 0, 8) \
+ DIV(K210_CLK_SPI1, K210_SYSCTL_THR1, 8, 8) \
+ DIV(K210_CLK_SPI2, K210_SYSCTL_THR1, 16, 8) \
+ DIV(K210_CLK_SPI3, K210_SYSCTL_THR1, 24, 8) \
+ DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2, 0, 8) \
+ DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2, 8, 8) \
+ DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16, 8) \
+ DIV(K210_CLK_I2S0, K210_SYSCTL_THR3, 0, 16) \
+ DIV(K210_CLK_I2S1, K210_SYSCTL_THR3, 16, 16) \
+ DIV(K210_CLK_I2S2, K210_SYSCTL_THR4, 0, 16) \
+ DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16, 8) \
+ DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24, 8) \
+ DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4, 0, 8) \
+ DIV(K210_CLK_I2C0, K210_SYSCTL_THR5, 8, 8) \
+ DIV(K210_CLK_I2C1, K210_SYSCTL_THR5, 16, 8) \
+ DIV(K210_CLK_I2C2, K210_SYSCTL_THR5, 24, 8) \
+ DIV(K210_CLK_WDT0, K210_SYSCTL_THR6, 0, 8) \
+ DIV(K210_CLK_WDT1, K210_SYSCTL_THR6, 8, 8)
+
+#define _DIVIFY(id) K210_CLK_DIV_##id
+#define DIVIFY(id) _DIVIFY(id)
+
+enum k210_div_ids {
+#define DIV_FLAGS(id, ...) DIVIFY(id),
+ DIV_LIST
+#undef DIV_FLAGS
+};
+
+struct k210_div_params {
+ u8 off;
+ u8 shift;
+ u8 width;
+ u8 flags;
+};
+
+static const struct k210_div_params k210_divs[] = {
+#define DIV_FLAGS(id, _off, _shift, _width, _flags) \
+ [DIVIFY(id)] = { \
+ .off = (_off), \
+ .shift = (_shift), \
+ .width = (_width), \
+ .flags = (_flags), \
+ },
+ DIV_LIST
+#undef DIV_FLAGS
+};
+
+#undef DIV
+#undef DIV_LIST
+
+#define GATE_LIST \
+ GATE(K210_CLK_CPU, K210_SYSCTL_EN_CENT, 0) \
+ GATE(K210_CLK_SRAM0, K210_SYSCTL_EN_CENT, 1) \
+ GATE(K210_CLK_SRAM1, K210_SYSCTL_EN_CENT, 2) \
+ GATE(K210_CLK_APB0, K210_SYSCTL_EN_CENT, 3) \
+ GATE(K210_CLK_APB1, K210_SYSCTL_EN_CENT, 4) \
+ GATE(K210_CLK_APB2, K210_SYSCTL_EN_CENT, 5) \
+ GATE(K210_CLK_ROM, K210_SYSCTL_EN_PERI, 0) \
+ GATE(K210_CLK_DMA, K210_SYSCTL_EN_PERI, 1) \
+ GATE(K210_CLK_AI, K210_SYSCTL_EN_PERI, 2) \
+ GATE(K210_CLK_DVP, K210_SYSCTL_EN_PERI, 3) \
+ GATE(K210_CLK_FFT, K210_SYSCTL_EN_PERI, 4) \
+ GATE(K210_CLK_GPIO, K210_SYSCTL_EN_PERI, 5) \
+ GATE(K210_CLK_SPI0, K210_SYSCTL_EN_PERI, 6) \
+ GATE(K210_CLK_SPI1, K210_SYSCTL_EN_PERI, 7) \
+ GATE(K210_CLK_SPI2, K210_SYSCTL_EN_PERI, 8) \
+ GATE(K210_CLK_SPI3, K210_SYSCTL_EN_PERI, 9) \
+ GATE(K210_CLK_I2S0, K210_SYSCTL_EN_PERI, 10) \
+ GATE(K210_CLK_I2S1, K210_SYSCTL_EN_PERI, 11) \
+ GATE(K210_CLK_I2S2, K210_SYSCTL_EN_PERI, 12) \
+ GATE(K210_CLK_I2C0, K210_SYSCTL_EN_PERI, 13) \
+ GATE(K210_CLK_I2C1, K210_SYSCTL_EN_PERI, 14) \
+ GATE(K210_CLK_I2C2, K210_SYSCTL_EN_PERI, 15) \
+ GATE(K210_CLK_UART1, K210_SYSCTL_EN_PERI, 16) \
+ GATE(K210_CLK_UART2, K210_SYSCTL_EN_PERI, 17) \
+ GATE(K210_CLK_UART3, K210_SYSCTL_EN_PERI, 18) \
+ GATE(K210_CLK_AES, K210_SYSCTL_EN_PERI, 19) \
+ GATE(K210_CLK_FPIOA, K210_SYSCTL_EN_PERI, 20) \
+ GATE(K210_CLK_TIMER0, K210_SYSCTL_EN_PERI, 21) \
+ GATE(K210_CLK_TIMER1, K210_SYSCTL_EN_PERI, 22) \
+ GATE(K210_CLK_TIMER2, K210_SYSCTL_EN_PERI, 23) \
+ GATE(K210_CLK_WDT0, K210_SYSCTL_EN_PERI, 24) \
+ GATE(K210_CLK_WDT1, K210_SYSCTL_EN_PERI, 25) \
+ GATE(K210_CLK_SHA, K210_SYSCTL_EN_PERI, 26) \
+ GATE(K210_CLK_OTP, K210_SYSCTL_EN_PERI, 27) \
+ GATE(K210_CLK_RTC, K210_SYSCTL_EN_PERI, 29)
+
+#define _GATEIFY(id) K210_CLK_GATE_##id
+#define GATEIFY(id) _GATEIFY(id)
+
+enum k210_gate_ids {
+#define GATE(id, ...) GATEIFY(id),
+ GATE_LIST
+#undef GATE
+};
+
+struct k210_gate_params {
+ u8 off;
+ u8 bit_idx;
+};
+
+static const struct k210_gate_params k210_gates[] = {
+#define GATE(id, _off, _idx) \
+ [GATEIFY(id)] = { \
+ .off = (_off), \
+ .bit_idx = (_idx), \
+ },
+ GATE_LIST
+#undef GATE
+};
+
+#undef GATE_LIST
+
+#define MUX(id, reg, shift, width) \
+ MUX_PARENTS(id, generic_sels, reg, shift, width)
+#define MUX_LIST \
+ MUX_PARENTS(K210_CLK_PLL2, pll2_sels, K210_SYSCTL_PLL2, 26, 2) \
+ MUX_PARENTS(K210_CLK_ACLK, aclk_sels, K210_SYSCTL_SEL0, 0, 1) \
+ MUX(K210_CLK_SPI3, K210_SYSCTL_SEL0, 12, 1) \
+ MUX(K210_CLK_TIMER0, K210_SYSCTL_SEL0, 13, 1) \
+ MUX(K210_CLK_TIMER1, K210_SYSCTL_SEL0, 14, 1) \
+ MUX(K210_CLK_TIMER2, K210_SYSCTL_SEL0, 15, 1)
+
+#define _MUXIFY(id) K210_CLK_MUX_##id
+#define MUXIFY(id) _MUXIFY(id)
+
+enum k210_mux_ids {
+#define MUX_PARENTS(id, ...) MUXIFY(id),
+ MUX_LIST
+#undef MUX_PARENTS
+ K210_CLK_MUX_NONE,
+};
+
+struct k210_mux_params {
+ const char *const *parent_names;
+ u8 num_parents;
+ u8 off;
+ u8 shift;
+ u8 width;
+};
+
+static const struct k210_mux_params k210_muxes[] = {
+#define MUX_PARENTS(id, parents, _off, _shift, _width) \
+ [MUXIFY(id)] = { \
+ .parent_names = (const char * const *)(parents), \
+ .num_parents = ARRAY_SIZE(parents), \
+ .off = (_off), \
+ .shift = (_shift), \
+ .width = (_width), \
+ },
+ MUX_LIST
+#undef MUX_PARENTS
+};
+
+#undef MUX
+#undef MUX_LIST
+
+struct k210_pll_params {
+ u8 off;
+ u8 lock_off;
+ u8 shift;
+ u8 width;
+};
+
+static const struct k210_pll_params k210_plls[] = {
+#define PLL(_off, _shift, _width) { \
+ .off = (_off), \
+ .lock_off = K210_SYSCTL_PLL_LOCK, \
+ .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
+};
+
+#define COMP(id) \
+ COMP_FULL(id, MUXIFY(id), DIVIFY(id), GATEIFY(id))
+#define COMP_NOMUX(id) \
+ COMP_FULL(id, K210_CLK_MUX_NONE, DIVIFY(id), GATEIFY(id))
+#define COMP_LIST \
+ COMP(K210_CLK_SPI3) \
+ COMP(K210_CLK_TIMER0) \
+ COMP(K210_CLK_TIMER1) \
+ COMP(K210_CLK_TIMER2) \
+ COMP_NOMUX(K210_CLK_SRAM0) \
+ COMP_NOMUX(K210_CLK_SRAM1) \
+ COMP_NOMUX(K210_CLK_ROM) \
+ COMP_NOMUX(K210_CLK_DVP) \
+ COMP_NOMUX(K210_CLK_APB0) \
+ COMP_NOMUX(K210_CLK_APB1) \
+ COMP_NOMUX(K210_CLK_APB2) \
+ COMP_NOMUX(K210_CLK_AI) \
+ COMP_NOMUX(K210_CLK_I2S0) \
+ COMP_NOMUX(K210_CLK_I2S1) \
+ COMP_NOMUX(K210_CLK_I2S2) \
+ COMP_NOMUX(K210_CLK_WDT0) \
+ COMP_NOMUX(K210_CLK_WDT1) \
+ COMP_NOMUX(K210_CLK_SPI0) \
+ COMP_NOMUX(K210_CLK_SPI1) \
+ COMP_NOMUX(K210_CLK_SPI2) \
+ COMP_NOMUX(K210_CLK_I2C0) \
+ COMP_NOMUX(K210_CLK_I2C1) \
+ COMP_NOMUX(K210_CLK_I2C2)
+
+#define _COMPIFY(id) K210_CLK_COMP_##id
+#define COMPIFY(id) _COMPIFY(id)
+
+enum k210_comp_ids {
+#define COMP_FULL(id, ...) COMPIFY(id),
+ COMP_LIST
+#undef COMP_FULL
+};
+
+struct k210_comp_params {
+ u8 mux;
+ u8 div;
+ u8 gate;
+};
+
+static const struct k210_comp_params k210_comps[] = {
+#define COMP_FULL(id, _mux, _div, _gate) \
+ [COMPIFY(id)] = { \
+ .mux = (_mux), \
+ .div = (_div), \
+ .gate = (_gate), \
+ },
+ COMP_LIST
+#undef COMP_FULL
+};
+
+#undef COMP
+#undef COMP_ID
+#undef COMP_NOMUX
+#undef COMP_NOMUX_ID
+#undef COMP_LIST
+
+static struct clk *k210_bypass_children = {
+ NULL,
+};
+
+/* Helper functions to create sub-clocks */
+static struct clk_mux *k210_create_mux(const struct k210_mux_params *params,
+ void *base)
+{
+ struct clk_mux *mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+
+ if (!mux)
+ return mux;
+
+ mux->reg = base + params->off;
+ mux->mask = BIT(params->width) - 1;
+ mux->shift = params->shift;
+ mux->parent_names = params->parent_names;
+ mux->num_parents = params->num_parents;
+
+ return mux;
+}
+
+static struct clk_divider *k210_create_div(const struct k210_div_params *params,
+ void *base)
+{
+ struct clk_divider *div = kzalloc(sizeof(*div), GFP_KERNEL);
+
+ if (!div)
+ return div;
+
+ div->reg = base + params->off;
+ div->shift = params->shift;
+ div->width = params->width;
+ div->flags = params->flags;
+
+ return div;
+}
+
+static struct clk_gate *k210_create_gate(const struct k210_gate_params *params,
+ void *base)
+{
+ struct clk_gate *gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+
+ if (!gate)
+ return gate;
+
+ gate->reg = base + params->off;
+ gate->bit_idx = params->bit_idx;
+
+ return gate;
+}
+
+static struct k210_pll *k210_create_pll(const struct k210_pll_params *params,
+ void *base)
+{
+ struct k210_pll *pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+
+ if (!pll)
+ return pll;
+
+ pll->reg = base + params->off;
+ pll->lock = base + params->lock_off;
+ pll->shift = params->shift;
+ pll->width = params->width;
+
+ return pll;
+}
+
+/* Create all sub-clocks, and then register the composite clock */
+static struct clk *k210_register_comp(const struct k210_comp_params *params,
+ void *base, const char *name,
+ const char *parent)
+{
+ const char *const *parent_names;
+ int num_parents;
+ struct clk *comp;
+ const struct clk_ops *mux_ops;
+ struct clk_mux *mux;
+ struct clk_divider *div;
+ struct clk_gate *gate;
+
+ if (params->mux == K210_CLK_MUX_NONE) {
+ if (!parent)
+ return ERR_PTR(-EINVAL);
+
+ mux_ops = NULL;
+ mux = NULL;
+ parent_names = &parent;
+ num_parents = 1;
+ } else {
+ mux_ops = &clk_mux_ops;
+ mux = k210_create_mux(&k210_muxes[params->mux], base);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ parent_names = mux->parent_names;
+ num_parents = mux->num_parents;
+ }
+
+ div = k210_create_div(&k210_divs[params->div], base);
+ if (!div) {
+ comp = ERR_PTR(-ENOMEM);
+ goto cleanup_mux;
+ }
+
+ gate = k210_create_gate(&k210_gates[params->gate], base);
+ if (!gate) {
+ comp = ERR_PTR(-ENOMEM);
+ goto cleanup_div;
+ }
+
+ comp = clk_register_composite(NULL, name, parent_names, num_parents,
+ &mux->clk, mux_ops,
+ &div->clk, &clk_divider_ops,
+ &gate->clk, &clk_gate_ops, 0);
+ if (IS_ERR(comp))
+ goto cleanup_gate;
+ return comp;
+
+cleanup_gate:
+ free(gate);
+cleanup_div:
+ free(div);
+cleanup_mux:
+ if (mux)
+ free(mux);
+ return comp;
+}
+
+static bool probed;
+
+static int k210_clk_probe(struct udevice *dev)
+{
+ int ret;
+ const char *in0;
+ struct clk *in0_clk, *bypass;
+ struct clk_mux *mux;
+ struct clk_divider *div;
+ struct k210_pll *pll;
+ void *base;
+
+ /*
+ * Only one instance of this driver allowed. This prevents weird bugs
+ * when the driver fails part-way through probing. Some clocks will
+ * already have been registered, and re-probing will register them
+ * again, creating a bunch of duplicates. Better error-handling/cleanup
+ * could fix this, but it's Probably Not Worth It (TM).
+ */
+ if (probed)
+ return -ENOTSUPP;
+
+ base = dev_read_addr_ptr(dev_get_parent(dev));
+ if (!base)
+ return -EINVAL;
+
+ in0_clk = kzalloc(sizeof(*in0_clk), GFP_KERNEL);
+ if (!in0_clk)
+ return -ENOMEM;
+
+ ret = clk_get_by_index(dev, 0, in0_clk);
+ if (ret)
+ return ret;
+ in0 = in0_clk->dev->name;
+
+ probed = true;
+
+ aclk_sels[0] = in0;
+ pll2_sels[0] = in0;
+
+ /*
+ * All PLLs have a broken bypass, but pll0 has the CPU downstream, so we
+ * need to manually reparent it whenever we configure pll0
+ */
+ pll = k210_create_pll(&k210_plls[0], base);
+ if (pll) {
+ bypass = k210_register_bypass("pll0", in0, &pll->clk,
+ &k210_pll_ops, in0_clk);
+ clk_dm(K210_CLK_PLL0, bypass);
+ } else {
+ return -ENOMEM;
+ }
+
+ {
+ const struct k210_pll_params *params = &k210_plls[1];
+
+ clk_dm(K210_CLK_PLL1,
+ k210_register_pll("pll1", in0, base + params->off,
+ base + params->lock_off, params->shift,
+ params->width));
+ }
+
+ /* PLL2 is muxed, so set up a composite clock */
+ mux = k210_create_mux(&k210_muxes[MUXIFY(K210_CLK_PLL2)], base);
+ pll = k210_create_pll(&k210_plls[2], base);
+ if (!mux || !pll) {
+ free(mux);
+ free(pll);
+ } else {
+ clk_dm(K210_CLK_PLL2,
+ clk_register_composite(NULL, "pll2", pll2_sels,
+ ARRAY_SIZE(pll2_sels),
+ &mux->clk, &clk_mux_ops,
+ &pll->clk, &k210_pll_ops,
+ &pll->clk, &k210_pll_ops, 0));
+ }
+
+ /* Half-frequency clocks for "even" dividers */
+ clk_dm(K210_CLK_IN0_H, k210_clk_half("in0_half", in0));
+ clk_dm(K210_CLK_PLL0_H, k210_clk_half("pll0_half", "pll0"));
+ clk_dm(K210_CLK_PLL2_H, k210_clk_half("pll2_half", "pll2"));
+
+ /* ACLK has no gate */
+ mux = k210_create_mux(&k210_muxes[MUXIFY(K210_CLK_ACLK)], base);
+ div = k210_create_div(&k210_divs[DIVIFY(K210_CLK_ACLK)], base);
+ if (!mux || !div) {
+ free(mux);
+ free(div);
+ } else {
+ struct clk *aclk =
+ clk_register_composite(NULL, "aclk", aclk_sels,
+ ARRAY_SIZE(aclk_sels),
+ &mux->clk, &clk_mux_ops,
+ &div->clk, &clk_divider_ops,
+ NULL, NULL, 0);
+ clk_dm(K210_CLK_ACLK, aclk);
+ if (!IS_ERR(aclk)) {
+ k210_bypass_children = aclk;
+ k210_bypass_set_children(bypass,
+ &k210_bypass_children, 1);
+ }
+ }
+
+#define REGISTER_COMP(id, name) \
+ clk_dm(id, \
+ k210_register_comp(&k210_comps[COMPIFY(id)], base, name, NULL))
+ REGISTER_COMP(K210_CLK_SPI3, "spi3");
+ REGISTER_COMP(K210_CLK_TIMER0, "timer0");
+ REGISTER_COMP(K210_CLK_TIMER1, "timer1");
+ REGISTER_COMP(K210_CLK_TIMER2, "timer2");
+#undef REGISTER_COMP
+
+ /* Dividing clocks, no mux */
+#define REGISTER_COMP_NOMUX(id, name, parent) \
+ clk_dm(id, \
+ k210_register_comp(&k210_comps[COMPIFY(id)], base, name, parent))
+ REGISTER_COMP_NOMUX(K210_CLK_SRAM0, "sram0", "aclk");
+ REGISTER_COMP_NOMUX(K210_CLK_SRAM1, "sram1", "aclk");
+ REGISTER_COMP_NOMUX(K210_CLK_ROM, "rom", "aclk");
+ REGISTER_COMP_NOMUX(K210_CLK_DVP, "dvp", "aclk");
+ REGISTER_COMP_NOMUX(K210_CLK_APB0, "apb0", "aclk");
+ REGISTER_COMP_NOMUX(K210_CLK_APB1, "apb1", "aclk");
+ REGISTER_COMP_NOMUX(K210_CLK_APB2, "apb2", "aclk");
+ REGISTER_COMP_NOMUX(K210_CLK_AI, "ai", "pll1");
+ REGISTER_COMP_NOMUX(K210_CLK_I2S0, "i2s0", "pll2_half");
+ REGISTER_COMP_NOMUX(K210_CLK_I2S1, "i2s1", "pll2_half");
+ REGISTER_COMP_NOMUX(K210_CLK_I2S2, "i2s2", "pll2_half");
+ REGISTER_COMP_NOMUX(K210_CLK_WDT0, "wdt0", "in0_half");
+ REGISTER_COMP_NOMUX(K210_CLK_WDT1, "wdt1", "in0_half");
+ REGISTER_COMP_NOMUX(K210_CLK_SPI0, "spi0", "pll0_half");
+ REGISTER_COMP_NOMUX(K210_CLK_SPI1, "spi1", "pll0_half");
+ REGISTER_COMP_NOMUX(K210_CLK_SPI2, "spi2", "pll0_half");
+ REGISTER_COMP_NOMUX(K210_CLK_I2C0, "i2c0", "pll0_half");
+ REGISTER_COMP_NOMUX(K210_CLK_I2C1, "i2c1", "pll0_half");
+ REGISTER_COMP_NOMUX(K210_CLK_I2C2, "i2c2", "pll0_half");
+#undef REGISTER_COMP_NOMUX
+
+ /* Dividing clocks */
+#define REGISTER_DIV(id, name, parent) do {\
+ const struct k210_div_params *params = &k210_divs[DIVIFY(id)]; \
+ clk_dm(id, \
+ clk_register_divider(NULL, name, parent, 0, base + params->off, \
+ params->shift, params->width, 0)); \
+} while (false)
+ REGISTER_DIV(K210_CLK_I2S0_M, "i2s0_m", "pll2_half");
+ REGISTER_DIV(K210_CLK_I2S1_M, "i2s1_m", "pll2_half");
+ REGISTER_DIV(K210_CLK_I2S2_M, "i2s2_m", "pll2_half");
+#undef REGISTER_DIV
+
+ /* Gated clocks */
+#define REGISTER_GATE(id, name, parent) do { \
+ const struct k210_gate_params *params = &k210_gates[GATEIFY(id)]; \
+ clk_dm(id, \
+ clk_register_gate(NULL, name, parent, 0, base + params->off, \
+ params->bit_idx, 0, NULL)); \
+} while (false)
+ REGISTER_GATE(K210_CLK_CPU, "cpu", "aclk");
+ REGISTER_GATE(K210_CLK_DMA, "dma", "aclk");
+ REGISTER_GATE(K210_CLK_FFT, "fft", "aclk");
+ REGISTER_GATE(K210_CLK_GPIO, "gpio", "apb0");
+ REGISTER_GATE(K210_CLK_UART1, "uart1", "apb0");
+ REGISTER_GATE(K210_CLK_UART2, "uart2", "apb0");
+ REGISTER_GATE(K210_CLK_UART3, "uart3", "apb0");
+ REGISTER_GATE(K210_CLK_FPIOA, "fpioa", "apb0");
+ REGISTER_GATE(K210_CLK_SHA, "sha", "apb0");
+ REGISTER_GATE(K210_CLK_AES, "aes", "apb1");
+ REGISTER_GATE(K210_CLK_OTP, "otp", "apb1");
+ REGISTER_GATE(K210_CLK_RTC, "rtc", in0);
+#undef REGISTER_GATE
+
+ return 0;
+}
+
+static const struct udevice_id k210_clk_ids[] = {
+ { .compatible = "kendryte,k210-clk" },
+ { },
+};
+
+U_BOOT_DRIVER(k210_clk) = {
+ .name = "k210_clk",
+ .id = UCLASS_CLK,
+ .of_match = k210_clk_ids,
+ .ops = &k210_clk_ops,
+ .probe = k210_clk_probe,
+};
diff --git a/drivers/clk/kendryte/pll.c b/drivers/clk/kendryte/pll.c
new file mode 100644
index 0000000000..19e358856a
--- /dev/null
+++ b/drivers/clk/kendryte/pll.c
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+#define LOG_CATEGORY UCLASS_CLK
+#include <kendryte/pll.h>
+
+#include <asm/io.h>
+/* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */
+#include <div64.h>
+#include <dt-bindings/clock/k210-sysctl.h>
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <log.h>
+#include <serial.h>
+
+#define CLK_K210_PLL "k210_clk_pll"
+
+#ifdef CONFIG_CLK_K210_SET_RATE
+static int k210_pll_enable(struct clk *clk);
+static int k210_pll_disable(struct clk *clk);
+
+/*
+ * The PLL included with the Kendryte K210 appears to be a True Circuits, Inc.
+ * General-Purpose PLL. The logical layout of the PLL with internal feedback is
+ * approximately the following:
+ *
+ * +---------------+
+ * |reference clock|
+ * +---------------+
+ * |
+ * v
+ * +--+
+ * |/r|
+ * +--+
+ * |
+ * v
+ * +-------------+
+ * |divided clock|
+ * +-------------+
+ * |
+ * v
+ * +--------------+
+ * |phase detector|<---+
+ * +--------------+ |
+ * | |
+ * v +--------------+
+ * +---+ |feedback clock|
+ * |VCO| +--------------+
+ * +---+ ^
+ * | +--+ |
+ * +--->|/f|---+
+ * | +--+
+ * v
+ * +---+
+ * |/od|
+ * +---+
+ * |
+ * v
+ * +------+
+ * |output|
+ * +------+
+ *
+ * The k210 PLLs have three factors: r, f, and od. Because of the feedback mode,
+ * the effect of the division by f is to multiply the input frequency. The
+ * equation for the output rate is
+ * rate = (rate_in * f) / (r * od).
+ * Moving knowns to one side of the equation, we get
+ * rate / rate_in = f / (r * od)
+ * Rearranging slightly,
+ * abs_error = abs((rate / rate_in) - (f / (r * od))).
+ * To get relative, error, we divide by the expected ratio
+ * error = abs((rate / rate_in) - (f / (r * od))) / (rate / rate_in).
+ * Simplifying,
+ * error = abs(1 - f / (r * od)) / (rate / rate_in)
+ * error = abs(1 - (f * rate_in) / (r * od * rate))
+ * Using the constants ratio = rate / rate_in and inv_ratio = rate_in / rate,
+ * error = abs((f * inv_ratio) / (r * od) - 1)
+ * This is the error used in evaluating parameters.
+ *
+ * r and od are four bits each, while f is six bits. Because r and od are
+ * multiplied together, instead of the full 256 values possible if both bits
+ * were used fully, there are only 97 distinct products. Combined with f, there
+ * are 6208 theoretical settings for the PLL. However, most of these settings
+ * can be ruled out immediately because they do not have the correct ratio.
+ *
+ * In addition to the constraint of approximating the desired ratio, parameters
+ * must also keep internal pll frequencies within acceptable ranges. The divided
+ * clock's minimum and maximum frequencies have a ratio of around 128. This
+ * leaves fairly substantial room to work with, especially since the only
+ * affected parameter is r. The VCO's minimum and maximum frequency have a ratio
+ * of 5, which is considerably more restrictive.
+ *
+ * The r and od factors are stored in a table. This is to make it easy to find
+ * the next-largest product. Some products have multiple factorizations, but
+ * only when one factor has at least a 2.5x ratio to the factors of the other
+ * factorization. This is because any smaller ratio would not make a difference
+ * when ensuring the VCO's frequency is within spec.
+ *
+ * Throughout the calculation function, fixed point arithmetic is used. Because
+ * the range of rate and rate_in may be up to 1.75 GHz, or around 2^30, 64-bit
+ * 32.32 fixed-point numbers are used to represent ratios. In general, to
+ * implement division, the numerator is first multiplied by 2^32. This gives a
+ * result where the whole number part is in the upper 32 bits, and the fraction
+ * is in the lower 32 bits.
+ *
+ * In general, rounding is done to the closest integer. This helps find the best
+ * approximation for the ratio. Rounding in one direction (e.g down) could cause
+ * the function to miss a better ratio with one of the parameters increased by
+ * one.
+ */
+
+/*
+ * The factors table was generated with the following python code:
+ *
+ * def p(x, y):
+ * return (1.0*x/y > 2.5) or (1.0*y/x > 2.5)
+ *
+ * factors = {}
+ * for i in range(1, 17):
+ * for j in range(1, 17):
+ * fs = factors.get(i*j) or []
+ * if fs == [] or all([
+ * (p(i, x) and p(i, y)) or (p(j, x) and p(j, y))
+ * for (x, y) in fs]):
+ * fs.append((i, j))
+ * factors[i*j] = fs
+ *
+ * for k, l in sorted(factors.items()):
+ * for v in l:
+ * print("PACK(%s, %s)," % v)
+ */
+#define PACK(r, od) (((((r) - 1) & 0xF) << 4) | (((od) - 1) & 0xF))
+#define UNPACK_R(val) ((((val) >> 4) & 0xF) + 1)
+#define UNPACK_OD(val) (((val) & 0xF) + 1)
+static const u8 factors[] = {
+ PACK(1, 1),
+ PACK(1, 2),
+ PACK(1, 3),
+ PACK(1, 4),
+ PACK(1, 5),
+ PACK(1, 6),
+ PACK(1, 7),
+ PACK(1, 8),
+ PACK(1, 9),
+ PACK(3, 3),
+ PACK(1, 10),
+ PACK(1, 11),
+ PACK(1, 12),
+ PACK(3, 4),
+ PACK(1, 13),
+ PACK(1, 14),
+ PACK(1, 15),
+ PACK(3, 5),
+ PACK(1, 16),
+ PACK(4, 4),
+ PACK(2, 9),
+ PACK(2, 10),
+ PACK(3, 7),
+ PACK(2, 11),
+ PACK(2, 12),
+ PACK(5, 5),
+ PACK(2, 13),
+ PACK(3, 9),
+ PACK(2, 14),
+ PACK(2, 15),
+ PACK(2, 16),
+ PACK(3, 11),
+ PACK(5, 7),
+ PACK(3, 12),
+ PACK(3, 13),
+ PACK(4, 10),
+ PACK(3, 14),
+ PACK(4, 11),
+ PACK(3, 15),
+ PACK(3, 16),
+ PACK(7, 7),
+ PACK(5, 10),
+ PACK(4, 13),
+ PACK(6, 9),
+ PACK(5, 11),
+ PACK(4, 14),
+ PACK(4, 15),
+ PACK(7, 9),
+ PACK(4, 16),
+ PACK(5, 13),
+ PACK(6, 11),
+ PACK(5, 14),
+ PACK(6, 12),
+ PACK(5, 15),
+ PACK(7, 11),
+ PACK(6, 13),
+ PACK(5, 16),
+ PACK(9, 9),
+ PACK(6, 14),
+ PACK(8, 11),
+ PACK(6, 15),
+ PACK(7, 13),
+ PACK(6, 16),
+ PACK(7, 14),
+ PACK(9, 11),
+ PACK(10, 10),
+ PACK(8, 13),
+ PACK(7, 15),
+ PACK(9, 12),
+ PACK(10, 11),
+ PACK(7, 16),
+ PACK(9, 13),
+ PACK(8, 15),
+ PACK(11, 11),
+ PACK(9, 14),
+ PACK(8, 16),
+ PACK(10, 13),
+ PACK(11, 12),
+ PACK(9, 15),
+ PACK(10, 14),
+ PACK(11, 13),
+ PACK(9, 16),
+ PACK(10, 15),
+ PACK(11, 14),
+ PACK(12, 13),
+ PACK(10, 16),
+ PACK(11, 15),
+ PACK(12, 14),
+ PACK(13, 13),
+ PACK(11, 16),
+ PACK(12, 15),
+ PACK(13, 14),
+ PACK(12, 16),
+ PACK(13, 15),
+ PACK(14, 14),
+ PACK(13, 16),
+ PACK(14, 15),
+ PACK(14, 16),
+ PACK(15, 15),
+ PACK(15, 16),
+ PACK(16, 16),
+};
+
+TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in,
+ struct k210_pll_config *best)
+{
+ int i;
+ s64 error, best_error;
+ u64 ratio, inv_ratio; /* fixed point 32.32 ratio of the rates */
+ u64 max_r;
+ u64 r, f, od;
+
+ /*
+ * Can't go over 1.75 GHz or under 21.25 MHz due to limitations on the
+ * VCO frequency. These are not the same limits as below because od can
+ * reduce the output frequency by 16.
+ */
+ if (rate > 1750000000 || rate < 21250000)
+ return -EINVAL;
+
+ /* Similar restrictions on the input rate */
+ if (rate_in > 1750000000 || rate_in < 13300000)
+ return -EINVAL;
+
+ ratio = DIV_ROUND_CLOSEST_ULL((u64)rate << 32, rate_in);
+ inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate);
+ /* Can't increase by more than 64 or reduce by more than 256 */
+ if (rate > rate_in && ratio > (64ULL << 32))
+ return -EINVAL;
+ else if (rate <= rate_in && inv_ratio > (256ULL << 32))
+ return -EINVAL;
+
+ /*
+ * The divided clock (rate_in / r) must stay between 1.75 GHz and 13.3
+ * MHz. There is no minimum, since the only way to get a higher input
+ * clock than 26 MHz is to use a clock generated by a PLL. Because PLLs
+ * cannot output frequencies greater than 1.75 GHz, the minimum would
+ * never be greater than one.
+ */
+ max_r = DIV_ROUND_DOWN_ULL(rate_in, 13300000);
+
+ /* Variables get immediately incremented, so start at -1th iteration */
+ i = -1;
+ f = 0;
+ r = 0;
+ od = 0;
+ best_error = S64_MAX;
+ error = best_error;
+ /* do-while here so we always try at least one ratio */
+ do {
+ /*
+ * Whether we swapped r and od while enforcing frequency limits
+ */
+ bool swapped = false;
+ u64 last_od = od;
+ u64 last_r = r;
+
+ /*
+ * Try the next largest value for f (or r and od) and
+ * recalculate the other parameters based on that
+ */
+ if (rate > rate_in) {
+ /*
+ * Skip factors of the same product if we already tried
+ * out that product
+ */
+ do {
+ i++;
+ r = UNPACK_R(factors[i]);
+ od = UNPACK_OD(factors[i]);
+ } while (i + 1 < ARRAY_SIZE(factors) &&
+ r * od == last_r * last_od);
+
+ /* Round close */
+ f = (r * od * ratio + BIT(31)) >> 32;
+ if (f > 64)
+ f = 64;
+ } else {
+ u64 tmp = ++f * inv_ratio;
+ bool round_up = !!(tmp & BIT(31));
+ u32 goal = (tmp >> 32) + round_up;
+ u32 err, last_err;
+
+ /* Get the next r/od pair in factors */
+ while (r * od < goal && i + 1 < ARRAY_SIZE(factors)) {
+ i++;
+ r = UNPACK_R(factors[i]);
+ od = UNPACK_OD(factors[i]);
+ }
+
+ /*
+ * This is a case of double rounding. If we rounded up
+ * above, we need to round down (in cases of ties) here.
+ * This prevents off-by-one errors resulting from
+ * choosing X+2 over X when X.Y rounds up to X+1 and
+ * there is no r * od = X+1. For the converse, when X.Y
+ * is rounded down to X, we should choose X+1 over X-1.
+ */
+ err = abs(r * od - goal);
+ last_err = abs(last_r * last_od - goal);
+ if (last_err < err || (round_up && last_err == err)) {
+ i--;
+ r = last_r;
+ od = last_od;
+ }
+ }
+
+ /*
+ * Enforce limits on internal clock frequencies. If we
+ * aren't in spec, try swapping r and od. If everything is
+ * in-spec, calculate the relative error.
+ */
+ while (true) {
+ /*
+ * Whether the intermediate frequencies are out-of-spec
+ */
+ bool out_of_spec = false;
+
+ if (r > max_r) {
+ out_of_spec = true;
+ } else {
+ /*
+ * There is no way to only divide once; we need
+ * to examine the frequency with and without the
+ * effect of od.
+ */
+ u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r);
+
+ if (vco > 1750000000 || vco < 340000000)
+ out_of_spec = true;
+ }
+
+ if (out_of_spec) {
+ if (!swapped) {
+ u64 tmp = r;
+
+ r = od;
+ od = tmp;
+ swapped = true;
+ continue;
+ } else {
+ /*
+ * Try looking ahead to see if there are
+ * additional factors for the same
+ * product.
+ */
+ if (i + 1 < ARRAY_SIZE(factors)) {
+ u64 new_r, new_od;
+
+ i++;
+ new_r = UNPACK_R(factors[i]);
+ new_od = UNPACK_OD(factors[i]);
+ if (r * od == new_r * new_od) {
+ r = new_r;
+ od = new_od;
+ swapped = false;
+ continue;
+ }
+ i--;
+ }
+ break;
+ }
+ }
+
+ error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio, r * od);
+ /* The lower 16 bits are spurious */
+ error = abs((error - BIT(32))) >> 16;
+
+ if (error < best_error) {
+ best->r = r;
+ best->f = f;
+ best->od = od;
+ best_error = error;
+ }
+ break;
+ }
+ } while (f < 64 && i + 1 < ARRAY_SIZE(factors) && error != 0);
+
+ if (best_error == S64_MAX)
+ return -EINVAL;
+
+ log_debug("best error %lld\n", best_error);
+ return 0;
+}
+
+static ulong k210_pll_set_rate(struct clk *clk, ulong rate)
+{
+ int err;
+ long long rate_in = clk_get_parent_rate(clk);
+ struct k210_pll_config config = {};
+ struct k210_pll *pll = to_k210_pll(clk);
+ u32 reg;
+
+ if (rate_in < 0)
+ return rate_in;
+
+ log_debug("Calculating parameters with rate=%lu and rate_in=%lld\n",
+ rate, rate_in);
+ err = k210_pll_calc_config(rate, rate_in, &config);
+ if (err)
+ return err;
+ log_debug("Got r=%u f=%u od=%u\n", config.r, config.f, config.od);
+
+ /*
+ * Don't use clk_disable as it might not actually disable the pll due to
+ * refcounting
+ */
+ k210_pll_disable(clk);
+
+ reg = readl(pll->reg);
+ reg &= ~K210_PLL_CLKR
+ & ~K210_PLL_CLKF
+ & ~K210_PLL_CLKOD
+ & ~K210_PLL_BWADJ;
+ reg |= FIELD_PREP(K210_PLL_CLKR, config.r - 1)
+ | 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);
+
+ err = k210_pll_enable(clk);
+ if (err)
+ return err;
+
+ serial_setbrg();
+ return clk_get_rate(clk);
+}
+#endif /* CONFIG_CLK_K210_SET_RATE */
+
+static ulong k210_pll_get_rate(struct clk *clk)
+{
+ 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);
+
+ if (rate_in < 0 || (reg & K210_PLL_BYPASS))
+ return rate_in;
+
+ if (!(reg & K210_PLL_PWRD))
+ return 0;
+
+ r = FIELD_GET(K210_PLL_CLKR, reg) + 1;
+ f = FIELD_GET(K210_PLL_CLKF, reg) + 1;
+ od = FIELD_GET(K210_PLL_CLKOD, reg) + 1;
+
+ return DIV_ROUND_DOWN_ULL(((u64)rate_in) * f, r * od);
+}
+
+/*
+ * 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)
+{
+ u32 mask = GENMASK(pll->width - 1, 0) << pll->shift;
+
+ while (true) {
+ u32 reg = readl(pll->lock);
+
+ if ((reg & mask) == mask)
+ break;
+
+ reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP);
+ writel(reg, pll->lock);
+ }
+}
+
+/* Adapted from sysctl_pll_enable */
+static int k210_pll_enable(struct clk *clk)
+{
+ struct k210_pll *pll = to_k210_pll(clk);
+ u32 reg = readl(pll->reg);
+
+ if ((reg | K210_PLL_PWRD) && !(reg | K210_PLL_RESET))
+ return 0;
+
+ reg |= K210_PLL_PWRD;
+ writel(reg, pll->reg);
+
+ /* Ensure reset is low before asserting it */
+ reg &= ~K210_PLL_RESET;
+ writel(reg, pll->reg);
+ reg |= K210_PLL_RESET;
+ writel(reg, pll->reg);
+ nop();
+ nop();
+ reg &= ~K210_PLL_RESET;
+ writel(reg, pll->reg);
+
+ k210_pll_waitfor_lock(pll);
+
+ reg &= ~K210_PLL_BYPASS;
+ writel(reg, pll->reg);
+
+ return 0;
+}
+
+static int k210_pll_disable(struct clk *clk)
+{
+ struct k210_pll *pll = to_k210_pll(clk);
+ u32 reg = readl(pll->reg);
+
+ /*
+ * Bypassing before powering off is important so child clocks don't stop
+ * working. This is especially important for pll0, the indirect parent
+ * of the cpu clock.
+ */
+ reg |= K210_PLL_BYPASS;
+ writel(reg, pll->reg);
+
+ reg &= ~K210_PLL_PWRD;
+ writel(reg, pll->reg);
+ 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;
+}
+
+struct clk *k210_register_pll(const char *name, const char *parent_name,
+ void __iomem *reg, void __iomem *lock, u8 shift,
+ u8 width)
+{
+ struct clk *clk;
+ struct k210_pll *pll;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+ pll->reg = reg;
+ pll->lock = lock;
+ pll->shift = shift;
+ pll->width = width;
+
+ clk = k210_register_pll_struct(name, parent_name, pll);
+ if (IS_ERR(clk))
+ kfree(pll);
+ return clk;
+}
+
+U_BOOT_DRIVER(k210_pll) = {
+ .name = CLK_K210_PLL,
+ .id = UCLASS_CLK,
+ .ops = &k210_pll_ops,
+};
diff --git a/drivers/clk/owl/clk_owl.c b/drivers/clk/owl/clk_owl.c
index 9715fce162..1999c87a33 100644
--- a/drivers/clk/owl/clk_owl.c
+++ b/drivers/clk/owl/clk_owl.c
@@ -87,6 +87,11 @@ int owl_clk_enable(struct clk *clk)
/* Enable UART3 interface clock */
setbits_le32(priv->base + CMU_DEVCLKEN1, CMU_DEVCLKEN1_UART3);
break;
+ case CLK_RMII_REF:
+ case CLK_ETHERNET:
+ setbits_le32(priv->base + CMU_DEVCLKEN1, CMU_DEVCLKEN1_ETH);
+ setbits_le32(priv->base + CMU_ETHERNETPLL, 5);
+ break;
default:
return -EINVAL;
}
@@ -112,6 +117,10 @@ int owl_clk_disable(struct clk *clk)
/* Disable UART3 interface clock */
clrbits_le32(priv->base + CMU_DEVCLKEN1, CMU_DEVCLKEN1_UART3);
break;
+ case CLK_RMII_REF:
+ case CLK_ETHERNET:
+ clrbits_le32(priv->base + CMU_DEVCLKEN1, CMU_DEVCLKEN1_ETH);
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/clk/owl/clk_owl.h b/drivers/clk/owl/clk_owl.h
index cf896bdb98..a01f81a6a7 100644
--- a/drivers/clk/owl/clk_owl.h
+++ b/drivers/clk/owl/clk_owl.h
@@ -62,6 +62,4 @@ struct owl_clk_priv {
#define CMU_DEVCLKEN1_UART5 BIT(21)
#define CMU_DEVCLKEN1_UART3 BIT(11)
-#define CMU_DEVCLKEN1_ETH_S700 BIT(23)
-
#endif