Hi Sean > Due to the large number of clocks, I decided to use the CCF. The overall > structure is modeled after the imx code. Clocks are stored in several > arrays. There are some translation macros (FOOIFY()) which allow for more > dense packing. A possible improvement could be to only store the > parameters we need, instead of the whole CCF struct. > > Signed-off-by: Sean Anderson <[email protected]> > ---
Please checkpatch and fix total: 4 errors, 4 warnings, 18 checks, 662 lines checked Thanks Rick > > Changes in v5: > - Don't unmap priv->reg > - Remove comment on APB clocks since it has been clarified by Kendryte > - Add i2s mclks > - Reorder clock ids to be continuous > - Rewrite to statically allocate all clocks. This has helped find several bugs > (since it is easy to see when a clock has the wrong register). > - Fix ACLK sometimes having the wrong parent > - Fix SPI3 having the wrong divider > - Prevent being probed multiple times on failure > > 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 > > MAINTAINERS | 7 + > .../mfd/kendryte,k210-sysctl.txt | 33 ++ > drivers/clk/kendryte/Kconfig | 2 +- > drivers/clk/kendryte/Makefile | 2 +- > drivers/clk/kendryte/clk.c | 478 ++++++++++++++++++ > include/dt-bindings/clock/k210-sysctl.h | 56 ++ > include/dt-bindings/mfd/k210-sysctl.h | 38 ++ > include/kendryte/clk.h | 35 ++ > 8 files changed, 649 insertions(+), 2 deletions(-) > create mode 100644 doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt > 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 > > diff --git a/MAINTAINERS b/MAINTAINERS > index 82e4159bec..8e9e0569ba 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -805,6 +805,13 @@ F: arch/riscv/ > F: cmd/riscv/ > F: tools/prelink-riscv.c > > +RISC-V KENDRYTE > +M: Sean Anderson <[email protected]> > +S: Maintained > +F: doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt > +F: drivers/clk/kendryte/ > +F: include/kendryte/ > + > RNG > M: Sughosh Ganu <[email protected]> > R: Heinrich Schuchardt <[email protected]> > diff --git a/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt > b/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt > new file mode 100644 > index 0000000000..5b24abcb62 > --- /dev/null > +++ b/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt > @@ -0,0 +1,33 @@ > +Kendryte K210 Sysctl > + > +This binding describes the K210 sysctl device, which contains many > miscellaneous > +registers controlling system functionality. This node is a register map and > can > +be reference by other bindings which need a phandle to the K210 sysctl > regmap. > + > +Required properties: > +- compatible: should be > + "kendryte,k210-sysctl", "syscon", "simple-mfd" > +- reg: address and length of the sysctl registers > +- reg-io-width: must be <4> > + > +Clock sub-node > + > +This node is a binding for the clock tree driver > + > +Required properties: > +- compatible: should be "kendryte,k210-clk" > +- clocks: phandle to the "in0" external oscillator > +- #clock-cells: must be <1> > + > +Example: > +sysctl: syscon@50440000 { > + compatible = "kendryte,k210-sysctl", "syscon", "simple-mfd"; > + reg = <0x50440000 0x100>; > + reg-io-width = <4>; > + > + sysclk: clock-controller { > + compatible = "kendryte,k210-clk"; > + clocks = <&in0>; > + #clock-cells = <1>; > + }; > +}; > diff --git a/drivers/clk/kendryte/Kconfig b/drivers/clk/kendryte/Kconfig > index 7b69c8afaf..073fca0781 100644 > --- a/drivers/clk/kendryte/Kconfig > +++ b/drivers/clk/kendryte/Kconfig > @@ -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. > > diff --git a/drivers/clk/kendryte/Makefile b/drivers/clk/kendryte/Makefile > index 47f682fce3..6fb68253ae 100644 > --- a/drivers/clk/kendryte/Makefile > +++ b/drivers/clk/kendryte/Makefile > @@ -1 +1 @@ > -obj-y += bypass.o pll.o > +obj-y += bypass.o clk.o pll.o > diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c > new file mode 100644 > index 0000000000..b01d246682 > --- /dev/null > +++ b/drivers/clk/kendryte/clk.c > @@ -0,0 +1,478 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2019-20 Sean Anderson <[email protected]> > + */ > +#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, > +}; > + > +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" }; > + > +#define DIV(id, reg, shift, width) DIV_FLAGS(id, reg, 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) > +#define DIV_FLAGS(id, ...) DIVIFY(id), > +enum k210_clk_div_ids { > + DIV_LIST > +}; > +#undef DIV_FLAGS > + > +#define DIV_FLAGS(id, _reg, _shift, _width, _flags) [DIVIFY(id)] = { \ > + .reg = (void *)(_reg), \ > + .shift = (_shift), \ > + .width = (_width), \ > + .flags = (_flags), \ > +}, > +static struct clk_divider k210_clk_dividers[] = { > + 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) > +#define GATE(id, ...) GATEIFY(id), > +enum k210_clk_gate_ids { > + GATE_LIST > +}; > +#undef GATE > + > +#define GATE(id, _reg, _idx) [GATEIFY(id)] = { \ > + .reg = (void *)(_reg), \ > + .bit_idx = (_idx), \ > +}, > +static struct clk_gate k210_clk_gates[] = { > + 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) > +#define MUX_PARENTS(id, ...) MUXIFY(id), > +enum k210_clk_mux_ids { > + MUX_LIST > +}; > +#undef MUX_PARENTS > + > +#define MUX_PARENTS(id, parents, _reg, _shift, _width) [MUXIFY(id)] = { \ > + .parent_names = (const char * const *)(parents), \ > + .num_parents = ARRAY_SIZE(parents), \ > + .reg = (void *)(_reg), \ > + .shift = (_shift), \ > + .mask = BIT(_width) - 1, \ > +}, > +static struct clk_mux k210_clk_muxes[] = { > + MUX_LIST > +}; > +#undef MUX_PARENTS > +#undef MUX > +#undef MUX_LIST > + > +#define PLL(_reg, _shift, _width) { \ > + .reg = (void *)(_reg), \ > + .lock = (void *)K210_SYSCTL_PLL_LOCK, \ > + .shift = (_shift), \ > + .width = (_width), \ > +} > +static struct k210_pll k210_clk_plls[] = { > + [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, mux, div, gate) \ > + COMP_FULL(id, &(mux)->clk, &clk_mux_ops, \ > + &(div)->clk, &clk_divider_ops, \ > + &(gate)->clk, &clk_gate_ops) > +#define COMP_ID(id) \ > + COMP(id, &k210_clk_muxes[MUXIFY(id)], \ > + &k210_clk_dividers[DIVIFY(id)], \ > + &k210_clk_gates[GATEIFY(id)]) > +#define COMP_NOMUX(id, div, gate) \ > + COMP_FULL(id, NULL, NULL, \ > + &(div)->clk, &clk_divider_ops, \ > + &(gate)->clk, &clk_gate_ops) > +#define COMP_NOMUX_ID(id) \ > + COMP_NOMUX(id, &k210_clk_dividers[DIVIFY(id)], \ > + &k210_clk_gates[GATEIFY(id)]) > +#define COMP_LIST \ > + COMP_FULL(K210_CLK_PLL2, \ > + &k210_clk_muxes[MUXIFY(K210_CLK_PLL2)].clk, &clk_mux_ops, \ > + &k210_clk_plls[2].clk, &k210_pll_ops, \ > + &k210_clk_plls[2].clk, &k210_pll_ops) \ > + COMP_FULL(K210_CLK_ACLK, \ > + &k210_clk_muxes[MUXIFY(K210_CLK_ACLK)].clk, &clk_mux_ops, \ > + &k210_clk_dividers[DIVIFY(K210_CLK_ACLK)].clk, \ > + &clk_divider_ops, \ > + NULL, NULL) \ > + COMP_ID(K210_CLK_SPI3) \ > + COMP_ID(K210_CLK_TIMER0) \ > + COMP_ID(K210_CLK_TIMER1) \ > + COMP_ID(K210_CLK_TIMER2) \ > + COMP_NOMUX_ID(K210_CLK_SRAM0) \ > + COMP_NOMUX_ID(K210_CLK_SRAM1) \ > + COMP_NOMUX_ID(K210_CLK_ROM) \ > + COMP_NOMUX_ID(K210_CLK_DVP) \ > + COMP_NOMUX_ID(K210_CLK_APB0) \ > + COMP_NOMUX_ID(K210_CLK_APB1) \ > + COMP_NOMUX_ID(K210_CLK_APB2) \ > + COMP_NOMUX_ID(K210_CLK_AI) \ > + COMP_NOMUX_ID(K210_CLK_I2S0) \ > + COMP_NOMUX_ID(K210_CLK_I2S1) \ > + COMP_NOMUX_ID(K210_CLK_I2S2) \ > + COMP_NOMUX_ID(K210_CLK_WDT0) \ > + COMP_NOMUX_ID(K210_CLK_WDT1) \ > + COMP_NOMUX_ID(K210_CLK_SPI0) \ > + COMP_NOMUX_ID(K210_CLK_SPI1) \ > + COMP_NOMUX_ID(K210_CLK_SPI2) \ > + COMP_NOMUX_ID(K210_CLK_I2C0) \ > + COMP_NOMUX_ID(K210_CLK_I2C1) \ > + COMP_NOMUX_ID(K210_CLK_I2C2) > + > +#define _COMPIFY(id) K210_CLK_COMP_##id > +#define COMPIFY(id) _COMPIFY(id) > +#define COMP_FULL(id, ...) COMPIFY(id), > +enum k210_clk_comp_ids { > + COMP_LIST > +}; > +#undef COMP_FULL > + > +#define COMP_FULL(id, _mux, _mux_ops, _div, _div_ops, _gate, _gate_ops) \ > +[COMPIFY(id)] = { \ > + .mux = (_mux), \ > + .mux_ops = (_mux_ops), \ > + .rate = (_div), \ > + .rate_ops = (_div_ops), \ > + .gate = (_gate), \ > + .gate_ops = (_gate_ops), \ > +}, > +static struct clk_composite k210_clk_comps[] = { > + COMP_LIST > +}; > +#undef COMP_FULL > +#undef COMP > +#undef COMP_ID > +#undef COMP_NOMUX > +#undef COMP_NOMUX_ID > +#undef COMP_LIST > + > +static struct clk *k210_clk_bypass_children = { > + &k210_clk_comps[COMPIFY(K210_CLK_ACLK)].clk, > +}; > + > +static struct clk *k210_clk_bypass_saved_parents = { > + NULL, > +}; > + > +static struct k210_bypass k210_clk_bypass = { > + .bypassee = &k210_clk_plls[0].clk, > + .bypassee_ops = &k210_pll_ops, > + .children = &k210_clk_bypass_children, > + .child_count = 1, > + .saved_parents = &k210_clk_bypass_saved_parents, > +}; > + > +static bool probed = false; > + > +static int k210_clk_probe(struct udevice *dev) > +{ > + int ret, i; > + const char *in0; > + struct clk *in0_clk; > + void *base; > + > + /* Only one instance of this driver allowed */ > + if (READ_ONCE(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; > + > + WRITE_ONCE(probed, true); > + > + aclk_sels[0] = in0; > + pll2_sels[0] = in0; > + > + /* Fixup registers to be absolute, rather than relative */ > +#define FIXUP_REGS(clocks) \ > + for (i = 0; i < ARRAY_SIZE(clocks); i++) \ > + clocks[i].reg += (ulong)base > + FIXUP_REGS(k210_clk_dividers); > + FIXUP_REGS(k210_clk_gates); > + FIXUP_REGS(k210_clk_muxes); > +#undef FIXUP_REGS > + for (i = 0; i < ARRAY_SIZE(k210_clk_plls); i++) { > + k210_clk_plls[i].reg += (ulong)base; > + k210_clk_plls[i].lock += (ulong)base; > + } > + > + /* > + * All PLLs have a broken bypass, but pll0 has the CPU downstream, so > we > + * need to manually reparent it whenever we configure pll0 > + */ > + k210_clk_bypass.alt = in0_clk; > + clk_dm(K210_CLK_PLL0, > + k210_register_bypass_struct("pll0", in0, &k210_clk_bypass)); > + clk_dm(K210_CLK_PLL1, > + k210_register_pll_struct("pll1", in0, &k210_clk_plls[1])); > + /* PLL2 is muxed, so set up a composite clock */ > + clk_dm(K210_CLK_PLL2, > + clk_register_composite_struct("pll2", pll2_sels, > + ARRAY_SIZE(pll2_sels), > + > &k210_clk_comps[COMPIFY(K210_CLK_PLL2)])); > + > + /* Half-frequency clocks for "even" dividers */ > + k210_clk_half("in0_half", in0); > + k210_clk_half("pll0_half", "pll0"); > + k210_clk_half("pll2_half", "pll2"); > + > + /* ACLK has no gate */ > + clk_dm(K210_CLK_ACLK, > + clk_register_composite_struct("aclk", aclk_sels, > + ARRAY_SIZE(aclk_sels), > + > &k210_clk_comps[COMPIFY(K210_CLK_ACLK)])); > + > +#define REGISTER_COMP(id, name) \ > + clk_dm(id, clk_register_composite_struct(name, generic_sels, \ > + ARRAY_SIZE(generic_sels), \ > + > &k210_clk_comps[COMPIFY(id)])) > + 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 COMP > + > + /* Dividing clocks, no mux */ > +#define REGISTER_COMP_NOMUX(id, name, _parent) do { \ > + const char *parent = _parent; \ > + clk_dm(id, \ > + clk_register_composite_struct(name, &parent, 1, \ > + &k210_clk_comps[COMPIFY(id)])); \ > +} while (false) > + 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) clk_dm(id, \ > + clk_register_divider_struct(name, parent, \ > + &k210_clk_dividers[DIVIFY(id)])) > + 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) \ > + clk_dm(id, clk_register_gate_struct(name, parent, \ > + &k210_clk_gates[GATEIFY(id)])) > + 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/include/dt-bindings/clock/k210-sysctl.h > b/include/dt-bindings/clock/k210-sysctl.h > new file mode 100644 > index 0000000000..16d67b282f > --- /dev/null > +++ b/include/dt-bindings/clock/k210-sysctl.h > @@ -0,0 +1,56 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Copyright (C) 2019-20 Sean Anderson <[email protected]> > + */ > + > +#ifndef CLOCK_K210_SYSCTL_H > +#define CLOCK_K210_SYSCTL_H > + > +/* > + * Arbitrary identifiers for clocks. > + */ > +#define K210_CLK_NONE 0 > +#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_I2S0_M 23 > +#define K210_CLK_I2S1_M 24 > +#define K210_CLK_I2S2_M 25 > +#define K210_CLK_I2C0 26 > +#define K210_CLK_I2C1 27 > +#define K210_CLK_I2C2 28 > +#define K210_CLK_UART1 29 > +#define K210_CLK_UART2 30 > +#define K210_CLK_UART3 31 > +#define K210_CLK_AES 32 > +#define K210_CLK_FPIOA 33 > +#define K210_CLK_TIMER0 34 > +#define K210_CLK_TIMER1 35 > +#define K210_CLK_TIMER2 36 > +#define K210_CLK_WDT0 37 > +#define K210_CLK_WDT1 38 > +#define K210_CLK_SHA 39 > +#define K210_CLK_OTP 40 > +#define K210_CLK_RTC 41 > +#define K210_CLK_ACLK 42 > + > +#endif /* CLOCK_K210_SYSCTL_H */ > diff --git a/include/dt-bindings/mfd/k210-sysctl.h > b/include/dt-bindings/mfd/k210-sysctl.h > new file mode 100644 > index 0000000000..e16d7302cd > --- /dev/null > +++ b/include/dt-bindings/mfd/k210-sysctl.h > @@ -0,0 +1,38 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Copyright (C) 2020 Sean Anderson <[email protected]> > + */ > + > +#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 */ > diff --git a/include/kendryte/clk.h b/include/kendryte/clk.h > new file mode 100644 > index 0000000000..9c6245d468 > --- /dev/null > +++ b/include/kendryte/clk.h > @@ -0,0 +1,35 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Copyright (C) 2019-20 Sean Anderson <[email protected]> > + */ > + > +#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); > +} > + > +static inline struct clk *k210_clk_div(const char *name, > + const char *parent_name, > + void __iomem *reg, u8 shift, u8 width) > +{ > + return clk_register_divider(NULL, name, parent_name, 0, reg, shift, > + width, 0); > +} > + > +#endif /* K210_CLK_H */ > -- > 2.25.0 >

