Message ID | 6dfd39a4-0721-eee4-7dbc-6ea2a7d7abf1@gmail.com |
---|---|
State | New |
Headers | show |
Series | riscv: Add Sipeed Maix support | expand |
Hi Sean, > 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> Reviewed-by: Lukasz Majewski <lukma at denx.de> > --- > Changes for 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 for 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 | 390 > ++++++++++++++++++++++++ drivers/clk/kendryte/clk.h | > 27 ++ include/dt-bindings/clock/k210-sysctl.h | 53 ++++ > include/dt-bindings/mfd/k210-sysctl.h | 38 +++ > 6 files changed, 510 insertions(+), 2 deletions(-) > create mode 100644 drivers/clk/kendryte/clk.c > create mode 100644 drivers/clk/kendryte/clk.h > create mode 100644 include/dt-bindings/clock/k210-sysctl.h > create mode 100644 include/dt-bindings/mfd/k210-sysctl.h > > 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 c56d93ea1c..d26bce954f 100644 > --- a/drivers/clk/kendryte/Makefile > +++ b/drivers/clk/kendryte/Makefile > @@ -1 +1 @@ > -obj-y += pll.o > +obj-y += clk.o pll.o > diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c > new file mode 100644 > index 0000000000..aaa7420c84 > --- /dev/null > +++ b/drivers/clk/kendryte/clk.c > @@ -0,0 +1,390 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2019 Sean Anderson <seanga2 at gmail.com> > + */ > +#include "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 "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 *generic_sels[] = { NULL, "pll0", }; > +static const char *aclk_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 *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 in0_clk; > + 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; > + generic_sels[0] = in0; > + pll2_sels[0] = in0; > + > + /* PLLs */ > + clk_dm(K210_CLK_PLL0, k210_clk_pll("pll0", in0, base + > K210_SYSCTL_PLL0, > + base + > K210_SYSCTL_PLL_LOCK, 0, 2)); > + 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("in0_half", in0); > + 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(aclk_sels, ARRAY_SIZE(aclk_sels), > + base + K210_SYSCTL_SEL0, 0, 1); > + if (!div || !mux) { > + kfree(div); > + kfree(mux); > + } else { > + clk_dm(K210_CLK_ACLK, > + clk_register_composite(NULL, "aclk", > aclk_sels, > + ARRAY_SIZE(aclk_sels), > + &mux->clk, > &clk_mux_ops, > + &div->clk, > &clk_divider_ops, > + NULL, NULL, 0)); > + } > + > + 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, > +}; > diff --git a/drivers/clk/kendryte/clk.h b/drivers/clk/kendryte/clk.h > new file mode 100644 > index 0000000000..17d0f5de1b > --- /dev/null > +++ b/drivers/clk/kendryte/clk.h > @@ -0,0 +1,27 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Copyright (C) 2019 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 */ > diff --git a/include/dt-bindings/clock/k210-sysctl.h > b/include/dt-bindings/clock/k210-sysctl.h new file mode 100644 > index 0000000000..7c471f9893 > --- /dev/null > +++ b/include/dt-bindings/clock/k210-sysctl.h > @@ -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 */ > diff --git a/include/dt-bindings/mfd/k210-sysctl.h > b/include/dt-bindings/mfd/k210-sysctl.h new file mode 100644 > index 0000000000..3457240f03 > --- /dev/null > +++ b/include/dt-bindings/mfd/k210-sysctl.h > @@ -0,0 +1,38 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Copyright (C) 2019 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 */ Best regards, Lukasz Majewski -- DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma at denx.de -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: <https://lists.denx.de/pipermail/u-boot/attachments/20200206/649d4bee/attachment.sig>
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 c56d93ea1c..d26bce954f 100644 --- a/drivers/clk/kendryte/Makefile +++ b/drivers/clk/kendryte/Makefile @@ -1 +1 @@ -obj-y += pll.o +obj-y += clk.o pll.o diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c new file mode 100644 index 0000000000..aaa7420c84 --- /dev/null +++ b/drivers/clk/kendryte/clk.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Sean Anderson <seanga2 at gmail.com> + */ +#include "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 "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 *generic_sels[] = { NULL, "pll0", }; +static const char *aclk_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 *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 in0_clk; + 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; + generic_sels[0] = in0; + pll2_sels[0] = in0; + + /* PLLs */ + clk_dm(K210_CLK_PLL0, k210_clk_pll("pll0", in0, base + K210_SYSCTL_PLL0, + base + K210_SYSCTL_PLL_LOCK, 0, 2)); + 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("in0_half", in0); + 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(aclk_sels, ARRAY_SIZE(aclk_sels), + base + K210_SYSCTL_SEL0, 0, 1); + if (!div || !mux) { + kfree(div); + kfree(mux); + } else { + clk_dm(K210_CLK_ACLK, + clk_register_composite(NULL, "aclk", aclk_sels, + ARRAY_SIZE(aclk_sels), + &mux->clk, &clk_mux_ops, + &div->clk, &clk_divider_ops, + NULL, NULL, 0)); + } + + 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, +}; diff --git a/drivers/clk/kendryte/clk.h b/drivers/clk/kendryte/clk.h new file mode 100644 index 0000000000..17d0f5de1b --- /dev/null +++ b/drivers/clk/kendryte/clk.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 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 */ diff --git a/include/dt-bindings/clock/k210-sysctl.h b/include/dt-bindings/clock/k210-sysctl.h new file mode 100644 index 0000000000..7c471f9893 --- /dev/null +++ b/include/dt-bindings/clock/k210-sysctl.h @@ -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 */ diff --git a/include/dt-bindings/mfd/k210-sysctl.h b/include/dt-bindings/mfd/k210-sysctl.h new file mode 100644 index 0000000000..3457240f03 --- /dev/null +++ b/include/dt-bindings/mfd/k210-sysctl.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 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 */
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 for 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 for 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 | 390 ++++++++++++++++++++++++ drivers/clk/kendryte/clk.h | 27 ++ include/dt-bindings/clock/k210-sysctl.h | 53 ++++ include/dt-bindings/mfd/k210-sysctl.h | 38 +++ 6 files changed, 510 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/kendryte/clk.c create mode 100644 drivers/clk/kendryte/clk.h create mode 100644 include/dt-bindings/clock/k210-sysctl.h create mode 100644 include/dt-bindings/mfd/k210-sysctl.h