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

Rick Chen rickchen36 at gmail.com
Wed Mar 4 07:58:24 CET 2020


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 at 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 at 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 at linaro.org>
>  R:     Heinrich Schuchardt <xypron.glpk at 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 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,
> +};
> +
> +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 at 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 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 */
> 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 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);
> +}
> +
> +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
>


More information about the U-Boot mailing list