[PATCH v5 07/33] clk: Add K210 clock support

Rick Chen
2020-03-04T06:58:24Z

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 <seanga2@gmail.com>
> ---

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 <seanga2@gmail.com>
> +S: Maintained
> +F: doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
> +F: drivers/clk/kendryte/
> +F: include/kendryte/
> +
> RNG
> M: Sughosh Ganu <sughosh.ganu@linaro.org>
> R: Heinrich Schuchardt <xypron.glpk@gmx.de>
> 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 at 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 <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>
> +
> +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 <seanga2@gmail.com>
> + */
> +
> +#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 <seanga2@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 */
> 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 <seanga2@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);
> +}
> +
> +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
>