@@ -1,6 +1,6 @@
config CLK_K210
bool "Clock support for Kendryte K210"
- depends on CLK && CLK_CCF
+ depends on CLK && CLK_CCF && CLK_COMPOSITE_CCF
help
This enables support clock driver for Kendryte K210 platforms.
@@ -1 +1 @@
-obj-y += bypass.o pll.o
+obj-y += bypass.o clk.o pll.o
new file mode 100644
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2 at 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>
+
+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,
+};
+
+/* The first clock is in0, which is filled in by k210_clk_probe */
+static const char * const generic_sels[] = { "in0_half", "pll0_half" };
+static const char *pll2_sels[] = { NULL, "pll0", "pll1" };
+
+static struct clk_divider *k210_clk_comp_div_flags(void __iomem *reg, u8 shift,
+ u8 width, u8 flags)
+{
+ struct clk_divider *div;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return div;
+ div->reg = reg;
+ div->shift = shift;
+ div->width = width;
+ div->flags = flags;
+ return div;
+}
+
+static inline struct clk_divider *k210_clk_comp_div(void __iomem *reg, u8 shift,
+ u8 width)
+{
+ return k210_clk_comp_div_flags(reg, shift, width, 0);
+}
+
+static struct clk_gate *k210_clk_comp_gate(void __iomem *reg, u8 bit_idx)
+{
+ struct clk_gate *gate;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return gate;
+ gate->reg = reg;
+ gate->bit_idx = bit_idx;
+ return gate;
+}
+
+static struct clk_mux *k210_clk_comp_mux(const char * const parent_names[],
+ u8 num_parents, void __iomem *reg,
+ u8 shift, u8 width)
+{
+ struct clk_mux *mux;
+
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return mux;
+ mux->reg = reg;
+ mux->mask = BIT(width) - 1;
+ mux->shift = shift;
+ mux->parent_names = parent_names;
+ mux->num_parents = num_parents;
+ return mux;
+}
+
+static struct clk *k210_clk_comp_nomux(const char *name, const char *parent,
+ struct clk_divider *div,
+ struct clk_gate *gate)
+{
+ if (!div || !gate) {
+ kfree(div);
+ kfree(gate);
+ return ERR_PTR(-ENOMEM);
+ }
+ return clk_register_composite(NULL, name, &parent, 1,
+ NULL, NULL,
+ &div->clk, &clk_divider_ops,
+ &gate->clk, &clk_gate_ops, 0);
+}
+
+static struct clk *k210_clk_comp(const char *name, struct clk_divider *div,
+ struct clk_gate *gate, struct clk_mux *mux)
+{
+ if (!div || !gate || !mux) {
+ kfree(div);
+ kfree(gate);
+ kfree(mux);
+ return ERR_PTR(-ENOMEM);
+ }
+ return clk_register_composite(NULL, name, generic_sels,
+ ARRAY_SIZE(generic_sels),
+ &mux->clk, &clk_mux_ops,
+ &div->clk, &clk_divider_ops,
+ &gate->clk, &clk_gate_ops, 0);
+}
+
+static int k210_clk_probe(struct udevice *dev)
+{
+ int err;
+ const char *in0;
+ struct clk **children;
+ struct clk *aclk;
+ struct clk *bypass;
+ struct clk in0_clk;
+ struct clk *in0_half;
+ struct clk_divider *div;
+ struct clk_gate *gate;
+ struct clk_mux *mux;
+ struct k210_pll *pll;
+ void *base;
+
+ base = dev_read_addr_ptr(dev_get_parent(dev));
+ if (!base)
+ return -EINVAL;
+
+ err = clk_get_by_index(dev, 0, &in0_clk);
+ if (err)
+ goto cleanup_base;
+ in0 = in0_clk.dev->name;
+ pll2_sels[0] = in0;
+
+ in0_half = k210_clk_half("in0_half", in0);
+
+ /* PLLs */
+ pll = k210_clk_comp_pll(base + K210_SYSCTL_PLL0,
+ base + K210_SYSCTL_PLL_LOCK, 0, 2);
+ /*
+ * All PLLs have a broken bypass, but pll0 has the CPU downstream, so we
+ * need to manually reparent it whenever we configure pll0
+ */
+ bypass = k210_clk_bypass("pll0", in0, &pll->clk, &k210_pll_ops,
+ in0_half);
+ clk_dm(K210_CLK_PLL0, bypass);
+ clk_dm(K210_CLK_PLL1, k210_clk_pll("pll1", in0, base + K210_SYSCTL_PLL1,
+ base + K210_SYSCTL_PLL_LOCK, 8, 1));
+ /* PLL2 is muxed, so set up a composite clock */
+ mux = k210_clk_comp_mux(pll2_sels, ARRAY_SIZE(pll2_sels),
+ base + K210_SYSCTL_PLL2, 26, 2);
+ pll = k210_clk_comp_pll(base + K210_SYSCTL_PLL2,
+ base + K210_SYSCTL_PLL_LOCK, 16, 1);
+ if (!mux || !pll) {
+ kfree(mux);
+ kfree(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 */
+ k210_clk_half("pll0_half", "pll0");
+ k210_clk_half("pll2_half", "pll2");
+
+ /* Muxed clocks */
+ div = k210_clk_comp_div_flags(base + K210_SYSCTL_SEL0, 1, 2,
+ CLK_DIVIDER_POWER_OF_TWO);
+ mux = k210_clk_comp_mux(generic_sels, ARRAY_SIZE(generic_sels),
+ base + K210_SYSCTL_SEL0, 0, 1);
+ /*
+ * aclk is the direct parent of the cpu clock, and needs to be
+ * reparented when pll0 is configured.
+ */
+ children = kzalloc(sizeof(*children), GFP_KERNEL);
+ if (!div || !mux || !children) {
+ kfree(div);
+ kfree(mux);
+ kfree(children);
+ } else {
+ aclk = clk_register_composite(NULL, "aclk", generic_sels,
+ ARRAY_SIZE(generic_sels),
+ &mux->clk, &clk_mux_ops,
+ &div->clk, &clk_divider_ops,
+ NULL, NULL, 0);
+ children[0] = aclk;
+ err = k210_bypass_set_children(bypass, children, 1);
+ if (!err)
+ clk_dm(K210_CLK_ACLK, aclk);
+ }
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_SEL0, 1, 2);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 9);
+ mux = k210_clk_comp_mux(generic_sels, ARRAY_SIZE(generic_sels),
+ base + K210_SYSCTL_SEL0, 12, 1);
+ clk_dm(K210_CLK_SPI3, k210_clk_comp("spi3", div, gate, mux));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR2, 8, 0);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 21);
+ mux = k210_clk_comp_mux(generic_sels, ARRAY_SIZE(generic_sels),
+ base + K210_SYSCTL_SEL0, 13, 1);
+ clk_dm(K210_CLK_TIMER0, k210_clk_comp("timer0", div, gate, mux));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR2, 8, 8);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 22);
+ mux = k210_clk_comp_mux(generic_sels, ARRAY_SIZE(generic_sels),
+ base + K210_SYSCTL_SEL0, 14, 1);
+ clk_dm(K210_CLK_TIMER1, k210_clk_comp("timer1", div, gate, mux));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR2, 8, 16);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 23);
+ mux = k210_clk_comp_mux(generic_sels, ARRAY_SIZE(generic_sels),
+ base + K210_SYSCTL_SEL0, 15, 1);
+ clk_dm(K210_CLK_TIMER2, k210_clk_comp("timer2", div, gate, mux));
+
+ /* Dividing clocks, no mux */
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR0, 0, 4);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_CENT, 1);
+ clk_dm(K210_CLK_SRAM0, k210_clk_comp_nomux("sram0", "aclk", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR0, 4, 4);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_CENT, 2);
+ clk_dm(K210_CLK_SRAM1, k210_clk_comp_nomux("sram1", "aclk", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR0, 16, 4);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 0);
+ clk_dm(K210_CLK_ROM, k210_clk_comp_nomux("rom", "aclk", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR0, 12, 4);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 3);
+ clk_dm(K210_CLK_DVP, k210_clk_comp_nomux("dvp", "aclk", div, gate));
+
+ /*
+ * XXX: the next three clocks may be using an even divider
+ * c.f. <https://github.com/kendryte/kendryte-standalone-sdk/issues/99>
+ */
+ div = k210_clk_comp_div(base + K210_SYSCTL_SEL0, 3, 3);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_CENT, 3);
+ clk_dm(K210_CLK_APB0, k210_clk_comp_nomux("apb0", "aclk", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_SEL0, 6, 3);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_CENT, 4);
+ clk_dm(K210_CLK_APB1, k210_clk_comp_nomux("apb1", "aclk", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_SEL0, 9, 3);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_CENT, 5);
+ clk_dm(K210_CLK_APB2, k210_clk_comp_nomux("apb2", "aclk", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR0, 8, 4);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 2);
+ clk_dm(K210_CLK_AI, k210_clk_comp_nomux("ai", "pll1", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR3, 0, 16);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 10);
+ clk_dm(K210_CLK_I2S0,
+ k210_clk_comp_nomux("i2s0", "pll2_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR3, 16, 16);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 11);
+ clk_dm(K210_CLK_I2S1,
+ k210_clk_comp_nomux("i2s1", "pll2_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR4, 0, 16);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 12);
+ clk_dm(K210_CLK_I2S2,
+ k210_clk_comp_nomux("i2s2", "pll2_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR6, 0, 8);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 24);
+ clk_dm(K210_CLK_WDT0,
+ k210_clk_comp_nomux("wdt0", "in0_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR6, 8, 8);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 25);
+ clk_dm(K210_CLK_WDT1,
+ k210_clk_comp_nomux("wdt1", "in0_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR1, 0, 8);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 6);
+ clk_dm(K210_CLK_SPI0,
+ k210_clk_comp_nomux("spi0", "pll0_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR1, 8, 8);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 7);
+ clk_dm(K210_CLK_SPI1,
+ k210_clk_comp_nomux("spi1", "pll0_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR1, 16, 8);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 8);
+ clk_dm(K210_CLK_SPI2,
+ k210_clk_comp_nomux("spi2", "pll0_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR5, 8, 8);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 13);
+ clk_dm(K210_CLK_I2C0,
+ k210_clk_comp_nomux("i2c0", "pll0_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR5, 16, 8);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 14);
+ clk_dm(K210_CLK_I2C1,
+ k210_clk_comp_nomux("i2c1", "pll0_half", div, gate));
+
+ div = k210_clk_comp_div(base + K210_SYSCTL_THR5, 24, 8);
+ gate = k210_clk_comp_gate(base + K210_SYSCTL_EN_PERI, 15);
+ clk_dm(K210_CLK_I2C2,
+ k210_clk_comp_nomux("i2c2", "pll0_half", div, gate));
+
+ /* Gated clocks */
+ clk_dm(K210_CLK_CPU,
+ k210_clk_gate("cpu", "aclk", base + K210_SYSCTL_EN_CENT, 0));
+ clk_dm(K210_CLK_DMA,
+ k210_clk_gate("dma", "aclk", base + K210_SYSCTL_EN_PERI, 1));
+ clk_dm(K210_CLK_FFT,
+ k210_clk_gate("fft", "aclk", base + K210_SYSCTL_EN_PERI, 4));
+ clk_dm(K210_CLK_GPIO,
+ k210_clk_gate("gpio", "apb0", base + K210_SYSCTL_EN_PERI, 5));
+ clk_dm(K210_CLK_UART1,
+ k210_clk_gate("uart1", "apb0", base + K210_SYSCTL_EN_PERI, 16));
+ clk_dm(K210_CLK_UART2,
+ k210_clk_gate("uart2", "apb0", base + K210_SYSCTL_EN_PERI, 17));
+ clk_dm(K210_CLK_UART3,
+ k210_clk_gate("uart3", "apb0", base + K210_SYSCTL_EN_PERI, 18));
+ clk_dm(K210_CLK_FPIOA,
+ k210_clk_gate("fpioa", "apb0", base + K210_SYSCTL_EN_PERI, 20));
+ clk_dm(K210_CLK_SHA,
+ k210_clk_gate("sha", "apb0", base + K210_SYSCTL_EN_PERI, 26));
+ clk_dm(K210_CLK_AES,
+ k210_clk_gate("aes", "apb1", base + K210_SYSCTL_EN_PERI, 19));
+ clk_dm(K210_CLK_OTP,
+ k210_clk_gate("otp", "apb1", base + K210_SYSCTL_EN_PERI, 27));
+ clk_dm(K210_CLK_RTC,
+ k210_clk_gate("rtc", in0, base + K210_SYSCTL_EN_PERI, 29));
+
+cleanup_base:
+ unmap_sysmem(base);
+ return err;
+}
+
+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,
+};
new file mode 100644
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019 Sean Anderson <seanga2 at gmail.com>
+ */
+
+#ifndef CLOCK_K210_SYSCTL_H
+#define CLOCK_K210_SYSCTL_H
+
+/*
+ * Arbitrary identifiers for clocks. 0 is unused since clk_enable thinks it
+ * means "no clock".
+ */
+#define K210_CLK_PLL0 1
+#define K210_CLK_PLL1 2
+#define K210_CLK_PLL2 3
+#define K210_CLK_CPU 4
+#define K210_CLK_SRAM0 5
+#define K210_CLK_SRAM1 6
+#define K210_CLK_APB0 7
+#define K210_CLK_APB1 8
+#define K210_CLK_APB2 9
+#define K210_CLK_ROM 10
+#define K210_CLK_DMA 11
+#define K210_CLK_AI 12
+#define K210_CLK_DVP 13
+#define K210_CLK_FFT 14
+#define K210_CLK_GPIO 15
+#define K210_CLK_SPI0 16
+#define K210_CLK_SPI1 17
+#define K210_CLK_SPI2 18
+#define K210_CLK_SPI3 19
+#define K210_CLK_I2S0 20
+#define K210_CLK_I2S1 21
+#define K210_CLK_I2S2 22
+#define K210_CLK_I2C0 23
+#define K210_CLK_I2C1 24
+#define K210_CLK_I2C2 25
+#define K210_CLK_UART1 26
+#define K210_CLK_UART2 27
+#define K210_CLK_UART3 28
+#define K210_CLK_AES 29
+#define K210_CLK_FPIOA 30
+#define K210_CLK_TIMER0 31
+#define K210_CLK_TIMER1 32
+#define K210_CLK_TIMER2 33
+#define K210_CLK_WDT0 34
+#define K210_CLK_WDT1 35
+#define K210_CLK_SHA 36
+#define K210_CLK_OTP 37
+#define K210_CLK_RTC 40
+#define K210_CLK_ACLK 41
+
+#endif /* CLOCK_K210_SYSCTL_H */
new file mode 100644
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2 at gmail.com>
+ */
+
+#ifndef K210_SYSCTL_H
+#define K210_SYSCTL_H
+
+/* Taken from kendryte-standalone-sdk/lib/drivers/include/sysctl.h */
+#define K210_SYSCTL_GIT_ID 0x00 /* Git short commit id */
+#define K210_SYSCTL_CLK_FREQ 0x04 /* System clock base frequency */
+#define K210_SYSCTL_PLL0 0x08 /* PLL0 controller */
+#define K210_SYSCTL_PLL1 0x0C /* PLL1 controller */
+#define K210_SYSCTL_PLL2 0x10 /* PLL2 controller */
+#define K210_SYSCTL_PLL_LOCK 0x18 /* PLL lock tester */
+#define K210_SYSCTL_ROM_ERROR 0x1C /* AXI ROM detector */
+#define K210_SYSCTL_SEL0 0x20 /* Clock select controller0 */
+#define K210_SYSCTL_SEL1 0x24 /* Clock select controller1 */
+#define K210_SYSCTL_EN_CENT 0x28 /* Central clock enable */
+#define K210_SYSCTL_EN_PERI 0x2C /* Peripheral clock enable */
+#define K210_SYSCTL_SOFT_RESET 0x30 /* Soft reset ctrl */
+#define K210_SYSCTL_PERI_RESET 0x34 /* Peripheral reset controller */
+#define K210_SYSCTL_THR0 0x38 /* Clock threshold controller 0 */
+#define K210_SYSCTL_THR1 0x3C /* Clock threshold controller 1 */
+#define K210_SYSCTL_THR2 0x40 /* Clock threshold controller 2 */
+#define K210_SYSCTL_THR3 0x44 /* Clock threshold controller 3 */
+#define K210_SYSCTL_THR4 0x48 /* Clock threshold controller 4 */
+#define K210_SYSCTL_THR5 0x4C /* Clock threshold controller 5 */
+#define K210_SYSCTL_THR6 0x50 /* Clock threshold controller 6 */
+#define K210_SYSCTL_MISC 0x54 /* Miscellaneous controller */
+#define K210_SYSCTL_PERI 0x58 /* Peripheral controller */
+#define K210_SYSCTL_SPI_SLEEP 0x5C /* SPI sleep controller */
+#define K210_SYSCTL_RESET_STAT 0x60 /* Reset source status */
+#define K210_SYSCTL_DMA_SEL0 0x64 /* DMA handshake selector */
+#define K210_SYSCTL_DMA_SEL1 0x68 /* DMA handshake selector */
+#define K210_SYSCTL_POWER_SEL 0x6C /* IO Power Mode Select controller */
+
+#endif /* K210_SYSCTL_H */
new file mode 100644
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2 at gmail.com>
+ */
+
+#ifndef K210_CLK_H
+#define K210_CLK_H
+
+#define LOG_CATEGORY UCLASS_CLK
+#include <linux/types.h>
+#include <linux/clk-provider.h>
+
+static inline struct clk *k210_clk_gate(const char *name,
+ const char *parent_name,
+ void __iomem *reg, u8 bit_idx)
+{
+ return clk_register_gate(NULL, name, parent_name, 0, reg, bit_idx, 0,
+ NULL);
+}
+
+static inline struct clk *k210_clk_half(const char *name,
+ const char *parent_name)
+{
+ return clk_register_fixed_factor(NULL, name, parent_name, 0, 1, 2);
+}
+
+#endif /* K210_CLK_H */
Due to the large number of clocks, I decided to use the CCF. The overall structure is modeled after the imx code. A common pattern is to create a composite clock composed of several component clocks. For these component clocks, the clk_register_* functions are not used, since they will be registered as part of the composite clock. To create these component clocks, several helper k210_clk_comp_* functions are used. This functionality seems like it would be useful to other drivers also creating composite clocks, so perhaps some general versions should be created. I am not particularly attached to the naming convention, suggestions are welcome. Signed-off-by: Sean Anderson <seanga2 at gmail.com> --- Changes in v4: - Reparent aclk before configuring pll0 - Update copyright - Lint Changes in v3: - Removed sysctl struct, replacing it with defines. This is to have the same interface to sysctl from C as from the device tree. - Fixed clocks having the same id - Fixed clocks not using the correct register/bits - Aligned the defines in headers Changes in v2: - Add clk.o to obj-y - Don't probe before relocation drivers/clk/kendryte/Kconfig | 2 +- drivers/clk/kendryte/Makefile | 2 +- drivers/clk/kendryte/clk.c | 409 ++++++++++++++++++++++++ include/dt-bindings/clock/k210-sysctl.h | 53 +++ include/dt-bindings/mfd/k210-sysctl.h | 38 +++ include/kendryte/clk.h | 27 ++ 6 files changed, 529 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/kendryte/clk.c create mode 100644 include/dt-bindings/clock/k210-sysctl.h create mode 100644 include/dt-bindings/mfd/k210-sysctl.h create mode 100644 include/kendryte/clk.h