From patchwork Wed Jan 15 22:47:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239625 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 17:47:59 -0500 Subject: [PATCH v2 01/11] clk: Always use the supplied struct clk In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: CCF clocks should always use the struct clock passed to their methods for extracting the driver-specific clock information struct. Previously, many functions would use the clk->dev->priv if the device was bound. This could cause problems with composite clocks. The individual clocks in a composite clock did not have the ->dev field filled in. This was fine, because the device-specific clock information would be used. However, since there was no ->dev, there was no way to get the parent clock. This caused the recalc_rate method of the CCF divider clock to fail. One option would be to use the clk->priv field to get the composite clock and from there get the appropriate parent device. However, this would tie the implementation to the composite clock. In general, different devices should not rely on the contents of ->priv from another device. The simple solution to this problem is to just always use the supplied struct clock. The composite clock now fills in the ->dev pointer of its child clocks. This allows child clocks to make calls like clk_get_parent() without issue. imx avoided the above problem by using a custom get_rate function with composite clocks. Signed-off-by: Sean Anderson --- drivers/clk/clk-composite.c | 8 ++++++++ drivers/clk/clk-divider.c | 6 ++---- drivers/clk/clk-fixed-factor.c | 3 +-- drivers/clk/clk-gate.c | 6 ++---- drivers/clk/clk-mux.c | 12 ++++-------- drivers/clk/imx/clk-gate2.c | 4 ++-- 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index a5626c33d1..d0f273d47f 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -145,6 +145,14 @@ struct clk *clk_register_composite(struct device *dev, const char *name, goto err; } + if (composite->mux) + composite->mux->dev = clk->dev; + if (composite->rate) + composite->rate->dev = clk->dev; + if (composite->gate) + composite->gate->dev = clk->dev; + + return clk; err: diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 822e09b084..bfa05f24a3 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -70,8 +70,7 @@ unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate, static ulong clk_divider_recalc_rate(struct clk *clk) { - struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ? - dev_get_clk_ptr(clk->dev) : clk); + struct clk_divider *divider = to_clk_divider(clk); unsigned long parent_rate = clk_get_parent_rate(clk); unsigned int val; @@ -150,8 +149,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate, static ulong clk_divider_set_rate(struct clk *clk, unsigned long rate) { - struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ? - dev_get_clk_ptr(clk->dev) : clk); + struct clk_divider *divider = to_clk_divider(clk); unsigned long parent_rate = clk_get_parent_rate(clk); int value; u32 val; diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 711b0588bc..d2401cf440 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -18,8 +18,7 @@ static ulong clk_factor_recalc_rate(struct clk *clk) { - struct clk_fixed_factor *fix = - to_clk_fixed_factor(dev_get_clk_ptr(clk->dev)); + struct clk_fixed_factor *fix = to_clk_fixed_factor(clk); unsigned long parent_rate = clk_get_parent_rate(clk); unsigned long long int rate; diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 70b8794554..b2933bc24a 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -43,8 +43,7 @@ */ static void clk_gate_endisable(struct clk *clk, int enable) { - struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ? - dev_get_clk_ptr(clk->dev) : clk); + struct clk_gate *gate = to_clk_gate(clk); int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; u32 reg; @@ -86,8 +85,7 @@ static int clk_gate_disable(struct clk *clk) int clk_gate_is_enabled(struct clk *clk) { - struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ? - dev_get_clk_ptr(clk->dev) : clk); + struct clk_gate *gate = to_clk_gate(clk); u32 reg; #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 5acc0b8cbd..67b4afef28 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -35,8 +35,7 @@ int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags, unsigned int val) { - struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ? - dev_get_clk_ptr(clk->dev) : clk); + struct clk_mux *mux = to_clk_mux(clk); int num_parents = mux->num_parents; if (table) { @@ -79,8 +78,7 @@ unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index) u8 clk_mux_get_parent(struct clk *clk) { - struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ? - dev_get_clk_ptr(clk->dev) : clk); + struct clk_mux *mux = to_clk_mux(clk); u32 val; #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) @@ -97,8 +95,7 @@ u8 clk_mux_get_parent(struct clk *clk) static int clk_fetch_parent_index(struct clk *clk, struct clk *parent) { - struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ? - dev_get_clk_ptr(clk->dev) : clk); + struct clk_mux *mux = to_clk_mux(clk); int i; @@ -115,8 +112,7 @@ static int clk_fetch_parent_index(struct clk *clk, static int clk_mux_set_parent(struct clk *clk, struct clk *parent) { - struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ? - dev_get_clk_ptr(clk->dev) : clk); + struct clk_mux *mux = to_clk_mux(clk); int index; u32 val; u32 reg; diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c index 1b9db6e791..e32c0cb53e 100644 --- a/drivers/clk/imx/clk-gate2.c +++ b/drivers/clk/imx/clk-gate2.c @@ -37,7 +37,7 @@ struct clk_gate2 { static int clk_gate2_enable(struct clk *clk) { - struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev)); + struct clk_gate2 *gate = to_clk_gate2(clk); u32 reg; reg = readl(gate->reg); @@ -50,7 +50,7 @@ static int clk_gate2_enable(struct clk *clk) static int clk_gate2_disable(struct clk *clk) { - struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev)); + struct clk_gate2 *gate = to_clk_gate2(clk); u32 reg; reg = readl(gate->reg); From patchwork Wed Jan 15 22:49:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239626 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 17:49:28 -0500 Subject: [PATCH v2 02/11] clk: Check that ops of composite clock components, exist before calling In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: clk_composite_ops was shared between all devices in the composite clock driver. If one clock had a feature (such as supporting set_parent) which another clock did not, it could call a null pointer dereference. This patch does three things 1. It adds null-pointer checks to all composite clock functions. 2. It makes clk_composite_ops const and sets its functions at compile-time. 3. It adds some basic sanity checks to num_parents. The combined effect of these changes is that any of mux, rate, or gate can be NULL, and composite clocks will still function normally. Previously, at least mux had to exist, since clk_composite_get_parent was used to determine the parent for clk_register. Signed-off-by: Sean Anderson --- drivers/clk/clk-composite.c | 57 +++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index d0f273d47f..ea9982fd57 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -22,7 +22,10 @@ static u8 clk_composite_get_parent(struct clk *clk) (struct clk *)dev_get_clk_ptr(clk->dev) : clk); struct clk *mux = composite->mux; - return clk_mux_get_parent(mux); + if (mux) + return clk_mux_get_parent(mux); + else + return 0; } static int clk_composite_set_parent(struct clk *clk, struct clk *parent) @@ -32,7 +35,10 @@ static int clk_composite_set_parent(struct clk *clk, struct clk *parent) const struct clk_ops *mux_ops = composite->mux_ops; struct clk *mux = composite->mux; - return mux_ops->set_parent(mux, parent); + if (mux && mux_ops) + return mux_ops->set_parent(mux, parent); + else + return -ENOTSUPP; } static unsigned long clk_composite_recalc_rate(struct clk *clk) @@ -42,7 +48,10 @@ static unsigned long clk_composite_recalc_rate(struct clk *clk) const struct clk_ops *rate_ops = composite->rate_ops; struct clk *rate = composite->rate; - return rate_ops->get_rate(rate); + if (rate && rate_ops) + return rate_ops->get_rate(rate); + else + return clk_get_parent_rate(clk); } static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate) @@ -52,7 +61,10 @@ static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate) const struct clk_ops *rate_ops = composite->rate_ops; struct clk *clk_rate = composite->rate; - return rate_ops->set_rate(clk_rate, rate); + if (rate && rate_ops) + return rate_ops->set_rate(clk_rate, rate); + else + return -ENOTSUPP; } static int clk_composite_enable(struct clk *clk) @@ -62,7 +74,10 @@ static int clk_composite_enable(struct clk *clk) const struct clk_ops *gate_ops = composite->gate_ops; struct clk *gate = composite->gate; - return gate_ops->enable(gate); + if (gate && gate_ops) + return gate_ops->enable(gate); + else + return -ENOTSUPP; } static int clk_composite_disable(struct clk *clk) @@ -72,15 +87,12 @@ static int clk_composite_disable(struct clk *clk) const struct clk_ops *gate_ops = composite->gate_ops; struct clk *gate = composite->gate; - gate_ops->disable(gate); - - return 0; + if (gate && gate_ops) + return gate_ops->disable(gate); + else + return -ENOTSUPP; } -struct clk_ops clk_composite_ops = { - /* This will be set according to clk_register_composite */ -}; - struct clk *clk_register_composite(struct device *dev, const char *name, const char * const *parent_names, int num_parents, struct clk *mux, @@ -94,7 +106,9 @@ struct clk *clk_register_composite(struct device *dev, const char *name, struct clk *clk; struct clk_composite *composite; int ret; - struct clk_ops *composite_ops = &clk_composite_ops; + + if (!num_parents || (num_parents != 1 && !mux)) + return ERR_PTR(-EINVAL); composite = kzalloc(sizeof(*composite), GFP_KERNEL); if (!composite) @@ -103,8 +117,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name, if (mux && mux_ops) { composite->mux = mux; composite->mux_ops = mux_ops; - if (mux_ops->set_parent) - composite_ops->set_parent = clk_composite_set_parent; mux->data = (ulong)composite; } @@ -113,11 +125,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name, clk = ERR_PTR(-EINVAL); goto err; } - composite_ops->get_rate = clk_composite_recalc_rate; - - /* .set_rate requires either .round_rate or .determine_rate */ - if (rate_ops->set_rate) - composite_ops->set_rate = clk_composite_set_rate; composite->rate = rate; composite->rate_ops = rate_ops; @@ -132,8 +139,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name, composite->gate = gate; composite->gate_ops = gate_ops; - composite_ops->enable = clk_composite_enable; - composite_ops->disable = clk_composite_disable; gate->data = (ulong)composite; } @@ -160,6 +165,14 @@ err: return clk; } +static const struct clk_ops clk_composite_ops = { + .set_parent = clk_composite_set_parent, + .get_rate = clk_composite_recalc_rate, + .set_rate = clk_composite_set_rate, + .enable = clk_composite_enable, + .disable = clk_composite_disable, +}; + U_BOOT_DRIVER(clk_composite) = { .name = UBOOT_DM_CLK_COMPOSITE, .id = UCLASS_CLK, From patchwork Wed Jan 15 22:50:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239627 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 17:50:46 -0500 Subject: [PATCH v2 03/11] riscv: Add headers for asm/global_data.h In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: <5203bcac-aea1-ad14-1c8e-e2a699be30b3@gmail.com> This header depended on bd_t and ulong, but did not include the appropriate headers. Signed-off-by: Sean Anderson --- arch/riscv/include/asm/global_data.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h index b74bd7e738..4f0c12b402 100644 --- a/arch/riscv/include/asm/global_data.h +++ b/arch/riscv/include/asm/global_data.h @@ -11,6 +11,8 @@ #define __ASM_GBL_DATA_H #include +#include +#include /* Architecture-specific global data */ struct arch_global_data { From patchwork Wed Jan 15 22:51:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239628 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 17:51:57 -0500 Subject: [PATCH v2 04/11] riscv: Add an option to default to RV64I In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: <256bf02e-5e6b-d903-f7ef-9fcbf05c9918@gmail.com> This allows 64-bit boards to default to the 64-bit instruction set without changing the current default of 32-bit. Signed-off-by: Sean Anderson --- arch/riscv/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 85e15ebffa..9a7b0334c2 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -60,8 +60,12 @@ source "arch/riscv/cpu/generic/Kconfig" # architecture-specific options below +config ARCH_DEFAULT_RV64I + bool + choice prompt "Base ISA" + default ARCH_RV64I if ARCH_DEFAULT_RV64I default ARCH_RV32I config ARCH_RV32I From patchwork Wed Jan 15 22:53:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239629 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 17:53:48 -0500 Subject: [PATCH v2 05/11] riscv: Add option to disable writes to mcounteren In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: <2eef694b-a166-3d62-bfab-a39e87cecf0a@gmail.com> On the kendryte k210, writes to mcounteren result in an illegal instruction exception. Signed-off-by: Sean Anderson --- Changes for v2: Moved forward in the patch series arch/riscv/Kconfig | 3 +++ arch/riscv/cpu/cpu.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 9a7b0334c2..4f8c62dcff 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -226,6 +226,9 @@ config XIP from a NOR flash memory without copying the code to ram. Say yes here if U-Boot boots from flash directly. +config SYS_RISCV_NOCOUNTER + bool "Disable accesses to the mcounteren CSR" + config STACK_SIZE_SHIFT int default 14 diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c index e457f6acbf..df9eae663c 100644 --- a/arch/riscv/cpu/cpu.c +++ b/arch/riscv/cpu/cpu.c @@ -89,7 +89,9 @@ int arch_cpu_init_dm(void) * Enable perf counters for cycle, time, * and instret counters only */ +#ifndef CONFIG_SYS_RISCV_NOCOUNTER csr_write(CSR_MCOUNTEREN, GENMASK(2, 0)); +#endif /* Disable paging */ if (supports_extension('s')) From patchwork Wed Jan 15 22:55:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239630 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 17:55:39 -0500 Subject: [PATCH v2 06/11] riscv: Fix incorrect cpu frequency on RV64 In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: <0b623be8-a24e-e200-c406-be54d84bb04a@gmail.com> The riscv_cpu_get_info function does not always zero-out cpu_freq. This can cause spurious higher frequencies. Signed-off-by Sean Anderson Reviewed-by: Lukas Auer --- Changes for v2: New. drivers/cpu/riscv_cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpu/riscv_cpu.c b/drivers/cpu/riscv_cpu.c index 28ad0aa30f..1e32bb5678 100644 --- a/drivers/cpu/riscv_cpu.c +++ b/drivers/cpu/riscv_cpu.c @@ -29,6 +29,8 @@ static int riscv_cpu_get_info(struct udevice *dev, struct cpu_info *info) { const char *mmu; + /* Zero out the frequency, in case sizeof(ulong) != sizeof(u32) */ + info->cpu_freq = 0; dev_read_u32(dev, "clock-frequency", (u32 *)&info->cpu_freq); mmu = dev_read_string(dev, "mmu-type"); From patchwork Wed Jan 15 23:04:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239631 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 18:04:27 -0500 Subject: [PATCH v2 07/11] riscv: Add initial Sipeed Maix support In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: <4c9aa605-013e-a32d-6d90-97286a0080dc@gmail.com> The Sipeed Maix series is a collection of boards built around the RISC-V Kendryte K210 processor. This processor contains several peripherals to accelerate neural network processing and other "ai" tasks. This includes a "KPU" neural network processor, an audio processor supporting beamforming reception, and a digital video port supporting capture and output at VGA resolution. Other peripherals include 8M of sram (accessible with and without caching); remappable pins, including 40 GPIOs; AES, FFT, and SHA256 accelerators; a DMA controller; and I2C, I2S, and SPI controllers. Maix peripherals vary, but include spi flash; on-board usb-serial bridges; ports for cameras, displays, and sd cards; and ESP32 chips. Currently, only the Sipeed Maix Bit V2.0 (bitm) is supported, but the boards are fairly similar. Documentation for Maix boards is located at . Documentation for the Kendryte K210 is located at . However, hardware details are rather lacking, so most technical reference has been taken from the standalone sdk located at . Signed-off-by: Sean Anderson --- Changes for v2: Select CONFIG_SYS_RISCV_NOCOUNTER. Imply CONFIG_CLK_K210. Remove spurious references to CONFIG_ARCH_K210. Remove many configs from defconfig where the defaults were fine. Add a few "not set" lines to suppress unneeded defaults. Reduce pre-reloc malloc space, now that clocks initialization happens later. arch/riscv/Kconfig | 4 ++ board/sipeed/maix/Kconfig | 41 +++++++++++++ board/sipeed/maix/MAINTAINERS | 13 +++++ board/sipeed/maix/Makefile | 5 ++ board/sipeed/maix/maix.c | 9 +++ configs/sipeed_maix_bitm_defconfig | 93 ++++++++++++++++++++++++++++++ include/configs/sipeed-maix.h | 19 ++++++ 7 files changed, 184 insertions(+) create mode 100644 board/sipeed/maix/Kconfig create mode 100644 board/sipeed/maix/MAINTAINERS create mode 100644 board/sipeed/maix/Makefile create mode 100644 board/sipeed/maix/maix.c create mode 100644 configs/sipeed_maix_bitm_defconfig create mode 100644 include/configs/sipeed-maix.h diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 4f8c62dcff..4c62b8dd77 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -20,6 +20,9 @@ config TARGET_QEMU_VIRT config TARGET_SIFIVE_FU540 bool "Support SiFive FU540 Board" +config TARGET_SIPEED_MAIX + bool "Support Sipeed Maix Board" + endchoice config SYS_ICACHE_OFF @@ -53,6 +56,7 @@ source "board/AndesTech/ax25-ae350/Kconfig" source "board/emulation/qemu-riscv/Kconfig" source "board/microchip/mpfs_icicle/Kconfig" source "board/sifive/fu540/Kconfig" +source "board/sipeed/maix/Kconfig" # platform-specific options below source "arch/riscv/cpu/ax25/Kconfig" diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig new file mode 100644 index 0000000000..9259eb34aa --- /dev/null +++ b/board/sipeed/maix/Kconfig @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2019 Sean Anderson + +if TARGET_SIPEED_MAIX + +config SYS_BOARD + default "maix" + +config SYS_VENDOR + default "sipeed" + +config SYS_CPU + default "generic" + +config SYS_CONFIG_NAME + default "sipeed-maix" + +config SYS_TEXT_BASE + default 0x80000000 + +config NR_CPUS + default 2 + +config NR_DRAM_BANKS + default 2 + +config BOARD_SPECIFIC_OPTIONS + def_bool y + select GENERIC_RISCV + select DM_SERIAL + select SIFIVE_SERIAL + select ARCH_DEFAULT_RV64I + select ENV_IS_NOWHERE + select SYS_RISCV_NOCOUNTER + imply SIFIVE_CLINT + imply SPI + imply DM_GPIO + imply CMD_GPIO + imply SYS_NS16550 + imply SYS_MALLOC_F +endif diff --git a/board/sipeed/maix/MAINTAINERS b/board/sipeed/maix/MAINTAINERS new file mode 100644 index 0000000000..217de45970 --- /dev/null +++ b/board/sipeed/maix/MAINTAINERS @@ -0,0 +1,13 @@ +Sipeed Maix BOARD +M: Sean Anderson +S: Maintained +F: arch/riscv/dts/k210.dtsi +F: arch/riscv/dts/k210-maix-bit.dts +F: arch/riscv/include/asm/k210_sysctl.h +F: arch/riscv/lib/k210_sysctl.c +F: board/sipeed/maix/ +F: configs/sipeed_maix_defconfig +F: drivers/clk/kendryte/ +F: include/configs/sipeed-maix.h +F: include/dt-bindings/clock/k210-sysctl.h +F: include/dt-bindings/reset/k210-sysctl.h diff --git a/board/sipeed/maix/Makefile b/board/sipeed/maix/Makefile new file mode 100644 index 0000000000..4acff5b31e --- /dev/null +++ b/board/sipeed/maix/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. + +obj-y += maix.o diff --git a/board/sipeed/maix/maix.c b/board/sipeed/maix/maix.c new file mode 100644 index 0000000000..f8e773acf7 --- /dev/null +++ b/board/sipeed/maix/maix.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Sean Anderson + */ + +int board_init(void) +{ + return 0; +} diff --git a/configs/sipeed_maix_bitm_defconfig b/configs/sipeed_maix_bitm_defconfig new file mode 100644 index 0000000000..f062cc8c58 --- /dev/null +++ b/configs/sipeed_maix_bitm_defconfig @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2019 Sean Anderson +CONFIG_CREATE_ARCH_SYMLINK=y +CONFIG_RISCV=y +CONFIG_SYS_ARCH="riscv" +CONFIG_SYS_CPU="generic" +CONFIG_SYS_VENDOR="sipeed" +CONFIG_SYS_BOARD="maix" +CONFIG_SYS_CONFIG_NAME="sipeed-maix" +CONFIG_SPL_LDSCRIPT="arch/riscv/cpu/u-boot-spl.lds" +CONFIG_SYS_TEXT_BASE=0x80000000 +CONFIG_SYS_MALLOC_F_LEN=0x1000 +CONFIG_BOARD_SPECIFIC_OPTIONS=y +CONFIG_NR_DRAM_BANKS=2 +CONFIG_BOOTSTAGE_STASH_ADDR=0 +CONFIG_64BIT=y +CONFIG_TARGET_SIPEED_MAIX=y +CONFIG_NR_CPUS=2 +CONFIG_GENERIC_RISCV=y +CONFIG_ARCH_DEFAULT_RV64I=y +CONFIG_ARCH_RV64I=y +CONFIG_CMODEL_MEDLOW=y +CONFIG_RISCV_MMODE=y +CONFIG_RISCV_ISA_C=y +CONFIG_RISCV_ISA_A=y +CONFIG_SIFIVE_CLINT=y +CONFIG_K210_SYSCTL=y +CONFIG_SYS_RISCV_NOCOUNTER=y +CONFIG_ARCH_K210=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYS_MALLOC_F=y +CONFIG_PHYS_64BIT=y +# CONFIG_ANDROID_BOOT_IMAGE is not set +# CONFIG_FIT is not set +# CONFIG_LEGACY_IMAGE_FORMAT is not set +CONFIG_LOG=y +CONFIG_ARCH_EARLY_INIT_R=y +CONFIG_CMDLINE=y +CONFIG_CMD_ENV_EXISTS=y +CONFIG_CMD_FLASH=y +CONFIG_CMD_SF=y +CONFIG_CMD_LOG=y +# CONFIG_AUTOBOOT is not set +# CONFIG_CMD_BDI is not set +# CONFIG_CMD_CONSOLE is not set +# CONFIG_CMD_BOOTD is not set +# CONFIG_CMD_BOOTM is not set +# CONFIG_CMD_BOOTZ is not set +CONFIG_CMD_BOOTI=y +CONFIG_BOOTM_LINUX=y +# CONFIG_CMD_ELF is not set +CONFIG_CMD_FDT=y +CONFIG_SUPPORT_OF_CONTROL=y +CONFIG_DTC=y +CONFIG_OF_CONTROL=y +CONFIG_OF_SEPARATE=y +CONFIG_ENV_IS_NOWHERE=y +# CONFIG_NET is not set +CONFIG_DM=y +CONFIG_REGMAP=y +CONFIG_SYSCON=y +CONFIG_SIMPLE_BUS=y +CONFIG_OF_TRANSLATE=y +CONFIG_CLK=y +CONFIG_CLK_CCF=y +CONFIG_CLK_COMPOSITE_CCF=y +CONFIG_CPU=y +CONFIG_CPU_RISCV=y +CONFIG_MMC=y +# CONFIG_INPUT is not set +CONFIG_DM_MMC=y +CONFIG_MMC_SPI=y +CONFIG_MMC_QUIRKS=y +CONFIG_MTD=y +CONFIG_DM_MTD=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_GIGADEVICE=y +# CONFIG_DM_ETH is not set +# CONFIG_PCI is not set +CONFIG_BAUDRATE=115200 +CONFIG_SERIAL_PRESENT=y +CONFIG_DM_SERIAL=y +CONFIG_SIFIVE_SERIAL=y +CONFIG_SPI=y +CONFIG_DM_SPI=y +CONFIG_SPI_MEM=y +CONFIG_DESIGNWARE_SPI=y +CONFIG_TIMER=y +CONFIG_RISCV_TIMER=y +CONFIG_PANIC_HANG=y +CONFIG_OF_LIBFDT=y +# CONFIG_EFI_LOADER is not set diff --git a/include/configs/sipeed-maix.h b/include/configs/sipeed-maix.h new file mode 100644 index 0000000000..598f7dfdd0 --- /dev/null +++ b/include/configs/sipeed-maix.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 Sean Anderson + */ + +#ifndef CONFIGS_SIPEED_MAIX_H +#define CONFIGS_SIPEED_MAIX_H + +#include + +#define CONFIG_SYS_LOAD_ADDR 0x80000000 +#define CONFIG_SYS_SDRAM_BASE CONFIG_SYS_LOAD_ADDR +#define CONFIG_SYS_SDRAM_SIZE SZ_8M +/* Start just below AI memory */ +#define CONFIG_SYS_INIT_SP_ADDR 0x805FFFFF +#define CONFIG_SYS_MALLOC_LEN SZ_1K +#define CONFIG_SYS_CACHELINE_SIZE 64 + +#endif /* CONFIGS_SIPEED_MAIX_H */ From patchwork Tue Jan 21 03:46:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239831 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Mon, 20 Jan 2020 22:46:20 -0500 Subject: [PATCH v2 08/11] riscv: Add device tree for K210 In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: <73c4ca8d-e702-15fd-6804-ecace54c7be1@gmail.com> There is a mirror of ram located at 0x4000000 which is un-cached. This is probably useful for DMA, but I am unsure of how to describe it in this device tree. The cache-line size is undocumented. Emphirical tests suggest that it is 32 bytes, but I've used 64-bytes to be on the safe side. Where possible, I have tried to find compatible drivers based on the layout of registers. However, I have not tested most of this functionality, and anything aside from clint0, uarths0, and sysctl should be considered descriptive at best. I would appreciate if anyone could help identify possibly compatible devices, especially for the timers, watchdogs, and rtc. The documentation for pinconf devices indicates that #pinctrl-cells is a required property, but I am unsure what to put for it. Should that be filled in by a board-specific device tree? The sysctl device has multiple different unrelated registers. For the moment, I have split off sub-devices for each functionality. In addition to clock and reset control, it also contains DMA handshake registers, and power registers. I have not described those at the moment, and I would appreciate suggestions on how to best describe them. I'm not sure what the value of spi-max-frequency should be for external sd-card slots. Suggestions are appreciated. Signed-off-by: Sean Anderson --- arch/riscv/dts/Makefile | 1 + arch/riscv/dts/k210-maix-bit.dts | 42 +++ arch/riscv/dts/k210.dtsi | 453 ++++++++++++++++++++++++ board/sipeed/maix/Kconfig | 3 + configs/sipeed_maix_bitm_defconfig | 1 + include/dt-bindings/clock/k210-sysctl.h | 54 +++ include/dt-bindings/reset/k210-sysctl.h | 38 ++ 7 files changed, 592 insertions(+) create mode 100644 arch/riscv/dts/k210-maix-bit.dts create mode 100644 arch/riscv/dts/k210.dtsi create mode 100644 include/dt-bindings/clock/k210-sysctl.h create mode 100644 include/dt-bindings/reset/k210-sysctl.h diff --git a/arch/riscv/dts/Makefile b/arch/riscv/dts/Makefile index 4f30e6936f..3a6f96c67d 100644 --- a/arch/riscv/dts/Makefile +++ b/arch/riscv/dts/Makefile @@ -2,6 +2,7 @@ dtb-$(CONFIG_TARGET_AX25_AE350) += ae350_32.dtb ae350_64.dtb dtb-$(CONFIG_TARGET_SIFIVE_FU540) += hifive-unleashed-a00.dtb +dtb-$(CONFIG_TARGET_SIPEED_MAIX) += k210-maix-bit.dtb targets += $(dtb-y) diff --git a/arch/riscv/dts/k210-maix-bit.dts b/arch/riscv/dts/k210-maix-bit.dts new file mode 100644 index 0000000000..a25aa34ff8 --- /dev/null +++ b/arch/riscv/dts/k210-maix-bit.dts @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Sean Anderson + */ + +/dts-v1/; + +#include "k210.dtsi" + +/ { + model = "Sipeed Maix Bit"; + compatible = "sipeed,maix-bit", "kendryte,k210"; + + chosen { + bootargs = "console=ttyS0,115200n8 debug loglevel=7"; + stdout-path = "serial0"; + }; +}; + +&uarths0 { + status = "okay"; +}; + +&spi0 { + status = "okay"; + slot at 0 { + compatible = "mmc-spi-slot"; + reg = <0>; + broken-cd; + disable-wp; + }; +}; + +&spi3 { + status = "okay"; + spi-flash at 0 { + compatible = "gd25lq64c", "gd25q64", "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <120000000>; + m25p,fast-read; + }; +}; diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi new file mode 100644 index 0000000000..1408d9b4a9 --- /dev/null +++ b/arch/riscv/dts/k210.dtsi @@ -0,0 +1,453 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Sean Anderson + */ + +#include +#include + +/ { + /* + * Although the K210 is a 64-bit CPU, the address bus is only 32-bits + * wide, and the upper half of all addresses is ignored. + */ + #address-cells = <1>; + #size-cells = <1>; + compatible = "kendryte,k210"; + + aliases { + serial0 = &uarths0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; + spi3 = &spi3; + }; + + clocks { + in0: oscillator { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <26000000>; + }; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + timebase-frequency = <7800000>; + cpu0: cpu at 0 { + device_type = "cpu"; + reg = <0>; + compatible = "riscv"; + riscv,isa = "rv64acdfim"; + mmu-type = "riscv,sv32"; + i-cache-size = <0x8000>; + i-cache-block-size = <64>; /* bogus */ + d-cache-size = <0x8000>; + d-cache-block-size = <64>; /* bogus */ + clocks = <&sysclk K210_CLK_CPU>; + clock-frequency = <390000000>; + cpu0_intc: interrupt-controller { + #interrupt-cells = <1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + cpu1: cpu at 1 { + device_type = "cpu"; + reg = <1>; + compatible = "riscv"; + riscv,isa = "rv64acdfim"; + mmu-type = "riscv,sv32"; + i-cache-size = <0x8000>; + i-cache-block-size = <64>; /* bogus */ + d-cache-size = <0x8000>; + d-cache-block-size = <64>; /* bogus */ + clocks = <&sysclk K210_CLK_CPU>; + clock-frequency = <390000000>; + cpu1_intc: interrupt-controller { + #interrupt-cells = <1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + }; + + sram0: memory at 80000000 { + device_type = "memory"; + reg = <0x80000000 0x400000>; + clocks = <&sysclk K210_CLK_SRAM0>; + }; + + sram1: memory at 80400000 { + device_type = "memory"; + reg = <0x80400000 0x400000>; + clocks = <&sysclk K210_CLK_SRAM1>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + ai_reserved: ai at 80600000 { + reg = <0x80600000 0x200000>; + reusable; + }; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "kendryte,k210-soc", "simple-bus"; + ranges; + interrupt-parent = <&plic0>; + + clint0: interrupt-controller at 2000000 { + compatible = "riscv,clint0"; + reg = <0x2000000 0xC000>; + interrupts-extended = <&cpu0_intc 3>, <&cpu1_intc 3>; + clocks = <&sysclk K210_CLK_CPU>; + }; + + plic0: interrupt-controller at c000000 { + #interrupt-cells = <1>; + interrupt-controller; + compatible = "riscv,plic1"; + reg = <0xC000000 0x03FFF008>; + interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 9>, + <&cpu1_intc 11>, <&cpu1_intc 9>; + riscv,ndev = <65>; + }; + + uarths0: serial at 38000000 { + compatible = "sifive,uart0"; + reg = <0x38000000 0x20>; + interrupts = <33>; + clocks = <&sysclk K210_CLK_CPU>; + status = "disabled"; + }; + + gpio0: gpio-controller at 38001000 { + #gpio-cells = <2>; + compatible = "none"; + reg = <0x38001000 0x44>; + gpio-controller; + interrupts = <34 35 36 37 38 39 40 41 + 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 + 58 59 60 61 62 63 64 65>; + clocks = <&sysclk K210_CLK_CPU>; + status = "disabled"; + }; + + kpu0: kpu at 40600000 { + compatible = "none"; + reg = <0x40800000 0x48>; + interrupts = <25>; + clocks = <&sysclk K210_CLK_AI>; + memory-region = <&ai_reserved>; + status = "disabled"; + }; + + fft0: fft at 42000000 { + compatible = "none"; + reg = <0x42000000 0x400000>; + interrupts = <26>; + clocks = <&sysclk K210_CLK_FFT>; + resets = <&sysrst K210_RST_FFT>; + status = "disabled"; + }; + + dmac0: dma-controller at 50000000 { + compatible = "snps,axi-dma-1.01a"; + reg = <0x50000000 0xD00>; + interrupts = <27 28 29 30 31 32>; + clocks = <&sysclk K210_CLK_DMA>, <&sysclk K210_CLK_DMA>; + clock-names = "core-clk", "cfgr-clk"; + resets = <&sysrst K210_RST_DMA>; + dma-channels = <6>; + snps,dma-masters = <2>; + snps,data-width = <5>; + snps,block-size = <0x400000 0x400000 0x400000 + 0x400000 0x400000 0x400000>; + snps,axi-max-burst-len = <256>; + status = "disabled"; + }; + + gpio1: gpio-controller at 50200000 { + #gpio-cells = <2>; + compatible = "none"; + reg = <0x50200000 0x6C>; + gpio-controller; + interrupts = <23>; + clocks = <&sysclk K210_CLK_GPIO>; + resets = <&sysrst K210_RST_GPIO>; + status = "disabled"; + }; + + uart1: serial at 50210000 { + compatible = "ns16550"; + reg = <50210000 0xc4>; + interrupts = <11>; + clocks = <&sysclk K210_CLK_UART1>; + resets = <&sysrst K210_RST_UART1>; + fifo-size = <8>; + no-loopback-test; + status = "disabled"; + }; + + uart2: serial at 50220000 { + compatible = "ns16550"; + reg = <50220000 0xc4>; + interrupts = <12>; + clocks = <&sysclk K210_CLK_UART2>; + resets = <&sysrst K210_RST_UART2>; + fifo-size = <8>; + no-loopback-test; + status = "disabled"; + }; + + uart3: serial at 50230000 { + compatible = "ns16550"; + reg = <50230000 0xc4>; + interrupts = <13>; + clocks = <&sysclk K210_CLK_UART3>; + resets = <&sysrst K210_RST_UART3>; + fifo-size = <8>; + no-loopback-test; + status = "disabled"; + }; + + spi2: spi at 50240000 { + compatible = "snps,dw-apb-ssi"; + spi-slave; + reg = <0x50240000 0x120>; + interrupts = <2>; + clocks = <&sysclk K210_CLK_SPI2>; + resets = <&sysrst K210_RST_SPI2>; + status = "disabled"; + }; + + i2s0: i2s at 50250000 { + compatible = "snps.designware-i2s"; + reg = <0x50250000 0x200>; + interrupts = <5>; + clocks = <&sysclk K210_CLK_I2S0>; + resets = <&sysrst K210_RST_I2S0>; + status = "disabled"; + }; + + apu0: sound at 520250200 { + compatible = "none"; + reg = <0x50250200 0x138>; + status = "disabled"; + }; + + i2s1: i2s at 50260000 { + compatible = "snps.designware-i2s"; + reg = <0x50260000 0x200>; + interrupts = <6>; + clocks = <&sysclk K210_CLK_I2S1>; + resets = <&sysrst K210_RST_I2S1>; + status = "disabled"; + }; + + i2s2: i2s at 50270000 { + compatible = "snps.designware-i2s"; + reg = <0x50270000 0x200>; + interrupts = <7>; + clocks = <&sysclk K210_CLK_I2S2>; + resets = <&sysrst K210_RST_I2S2>; + status = "disabled"; + }; + + i2c0: i2c at 50280000 { + compatible = "snps,designware-i2c"; + reg = <0x50280000 0x100>; + interrupts = <8>; + clocks = <&sysclk K210_CLK_I2C0>; + resets = <&sysrst K210_RST_I2C0>; + status = "disabled"; + }; + + i2c1: i2c at 50290000 { + compatible = "snps,designware-i2c"; + reg = <0x50290000 0x100>; + interrupts = <9>; + clocks = <&sysclk K210_CLK_I2C1>; + resets = <&sysrst K210_RST_I2C1>; + status = "disabled"; + }; + + + i2c2: i2c at 502A0000 { + compatible = "snps,designware-i2c"; + reg = <0x502A0000 0x100>; + interrupts = <10>; + clocks = <&sysclk K210_CLK_I2C2>; + resets = <&sysrst K210_RST_I2C2>; + status = "disabled"; + }; + + fpioa: pinmux at 502B0000 { + compatible = "pinconf-single"; + reg = <0x502B0000 0x100>; + //#pinctrl-cells = ; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x7fffff>; + clocks = <&sysclk K210_CLK_FPIOA>; + resets = <&sysrst K210_RST_FPIOA>; + status = "disabled"; + }; + + sha256: sha256 at 502C0000 { + compatible = "none"; + reg = <0x502C0000 0x38>; + clocks = <&sysclk K210_CLK_SHA>; + resets = <&sysrst K210_RST_SHA>; + status = "disabled"; + }; + + timer0: timer at 502D0000 { + compatible = "none"; + reg = <0x502D0000 0xC0>; + interrupts = <14 15>; + clocks = <&sysclk K210_CLK_TIMER0>; + resets = <&sysrst K210_RST_TIMER0>; + status = "disabled"; + }; + + timer1: timer at 502E0000 { + compatible = "none"; + reg = <0x502E0000 0xC0>; + interrupts = <16 17>; + clocks = <&sysclk K210_CLK_TIMER1>; + resets = <&sysrst K210_RST_TIMER1>; + status = "disabled"; + }; + + timer2: timer at 502F0000 { + compatible = "none"; + reg = <0x502F0000 0xC0>; + interrupts = <18 19>; + clocks = <&sysclk K210_CLK_TIMER2>; + resets = <&sysrst K210_RST_TIMER2>; + status = "disabled"; + }; + + wdt0: watchdog at 50400000 { + compatible = "none"; + reg = <0x50400000 0x100>; + interrupts = <21>; + clocks = <&sysclk K210_CLK_WDT0>; + resets = <&sysrst K210_RST_WDT0>; + status = "disabled"; + }; + + wdt1: watchdog at 50410000 { + compatible = "none"; + reg = <0x50410000 0x100>; + interrupts = <22>; + clocks = <&sysclk K210_CLK_WDT1>; + resets = <&sysrst K210_RST_WDT1>; + status = "disabled"; + }; + + dvp0: camera at 50430000 { + compatible = "none"; + reg = <0x50430000 0x2c>; + interrupts = <24>; + clocks = <&sysclk K210_CLK_DVP>; + resets = <&sysrst K210_RST_DVP>; + status = "disabled"; + }; + + sysctl: sysctl at 50440000 { + compatible = "kendryte,k210-sysctl", "syscon"; + reg = <0x50440000 0x80>; + + sysclk: clock-controller { + compatible = "kendryte,k210-clk"; + clocks = <&in0>; + #clock-cells = <1>; + }; + + sysrst: reset-controller { + compatible = "kendryte,k210-rst"; + #reset-cells = <1>; + }; + }; + + aes0: aes at 50450000 { + compatible = "none"; + reg = <0x50450000 0x94>; + clocks = <&sysclk K210_CLK_AES>; + resets = <&sysrst K210_RST_AES>; + status = "disabled"; + }; + + rtc: rts at 50460000 { + compatible = "none"; + reg = <0x50460000 0x2C>; + clocks = <&in0>; + resets = <&sysrst K210_RST_RTC>; + interrupts = <20>; + status = "disabled"; + }; + + spi0: spi at 52000000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dw-apb-ssi"; + reg = <0x52000000 0x120>; + interrupts = <1>; + clocks = <&sysclk K210_CLK_SPI0>; + clock-names = "ssi_clk"; + resets = <&sysrst K210_RST_SPI0>; + num-cs = <4>; + reg-io-width = <4>; + status = "disabled"; + }; + + spi1: spi at 53000000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dw-apb-ssi"; + reg = <0x53000000 0x120>; + interrupts = <2>; + clocks = <&sysclk K210_CLK_SPI1>; + clock-names = "ssi_clk"; + resets = <&sysrst K210_RST_SPI1>; + num-cs = <4>; + reg-io-width = <4>; + status = "disabled"; + }; + + spi3: spi at 54000000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dw-apb-ssi"; + reg = <0x54000000 0x120>; + interrupts = <4>; + clocks = <&sysclk K210_CLK_SPI3>; + clock-names = "ssi_clk"; + assigned-clocks = <&sysclk K210_CLK_SPI3>; + assigned-clock-parents = <&sysclk K210_CLK_PLL0>; + resets = <&sysrst K210_RST_SPI3>; + num-cs = <4>; + reg-io-width = <4>; + status = "disabled"; + }; + + rom0: nvmem at 88000000 { + reg = <0x88000000 0x4000>; + read-only; + clocks = <&sysclk K210_CLK_ROM>; + resets = <&sysrst K210_RST_ROM>; + }; + }; +}; diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig index 9259eb34aa..efeaddf317 100644 --- a/board/sipeed/maix/Kconfig +++ b/board/sipeed/maix/Kconfig @@ -18,6 +18,9 @@ config SYS_CONFIG_NAME config SYS_TEXT_BASE default 0x80000000 +config DEFAULT_DEVICE_TREE + default "k210-maix-bit" + config NR_CPUS default 2 diff --git a/configs/sipeed_maix_bitm_defconfig b/configs/sipeed_maix_bitm_defconfig index f062cc8c58..ecd69c0873 100644 --- a/configs/sipeed_maix_bitm_defconfig +++ b/configs/sipeed_maix_bitm_defconfig @@ -15,6 +15,7 @@ CONFIG_NR_DRAM_BANKS=2 CONFIG_BOOTSTAGE_STASH_ADDR=0 CONFIG_64BIT=y CONFIG_TARGET_SIPEED_MAIX=y +CONFIG_DEFAULT_DEVICE_TREE="k210-maix-bit" CONFIG_NR_CPUS=2 CONFIG_GENERIC_RISCV=y CONFIG_ARCH_DEFAULT_RV64I=y diff --git a/include/dt-bindings/clock/k210-sysctl.h b/include/dt-bindings/clock/k210-sysctl.h new file mode 100644 index 0000000000..308974baf8 --- /dev/null +++ b/include/dt-bindings/clock/k210-sysctl.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 Sean Anderson + */ + +#ifndef CLOCK_K210_SYSCTL_H +#define CLOCK_K210_SYSCTL_H + +/* Clocks as defined by kendryte-standalone-sdk/lib/drivers/include/sysctl.h */ +#define K210_CLK_PLL0 0 +#define K210_CLK_PLL1 1 +#define K210_CLK_PLL2 2 +#define K210_CLK_CPU 3 +#define K210_CLK_SRAM0 4 +#define K210_CLK_SRAM1 5 +#define K210_CLK_APB0 6 +#define K210_CLK_APB1 7 +#define K210_CLK_APB2 8 +#define K210_CLK_ROM 9 +#define K210_CLK_DMA 10 +#define K210_CLK_AI 11 +#define K210_CLK_DVP 12 +#define K210_CLK_FFT 13 +#define K210_CLK_GPIO 14 +#define K210_CLK_SPI0 15 +#define K210_CLK_SPI1 16 +#define K210_CLK_SPI2 17 +#define K210_CLK_SPI3 18 +#define K210_CLK_I2S0 19 +#define K210_CLK_I2S1 20 +#define K210_CLK_I2S2 21 +#define K210_CLK_I2C0 22 +#define K210_CLK_I2C1 23 +#define K210_CLK_I2C2 24 +#define K210_CLK_UART1 25 +#define K210_CLK_UART2 26 +#define K210_CLK_UART3 27 +#define K210_CLK_AES 28 +#define K210_CLK_FPIOA 29 +#define K210_CLK_TIMER0 30 +#define K210_CLK_TIMER1 31 +#define K210_CLK_TIMER2 32 +#define K210_CLK_WDT0 33 +#define K210_CLK_WDT1 34 +#define K210_CLK_SHA 35 +#define K210_CLK_OTP 36 +#define K210_CLK_RTC 37 +#define K210_CLK_ACLK 40 +#define K210_CLK_MAX 40 + +#define K210_CLK_HCLK 41 /* Unused */ +#define K210_CLK_IN0 42 /* Defined elsewhere in the device tree */ + +#endif /* CLOCK_K210_SYSCTL_H */ diff --git a/include/dt-bindings/reset/k210-sysctl.h b/include/dt-bindings/reset/k210-sysctl.h new file mode 100644 index 0000000000..670e74daaf --- /dev/null +++ b/include/dt-bindings/reset/k210-sysctl.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 Sean Anderson + */ + +#ifndef RESET_K210_SYSCTL_H +#define RESET_K210_SYSCTL_H + +#define K210_RST_ROM 0 +#define K210_RST_DMA 1 +#define K210_RST_AI 2 +#define K210_RST_DVP 3 +#define K210_RST_FFT 4 +#define K210_RST_GPIO 5 +#define K210_RST_SPI0 6 +#define K210_RST_SPI1 7 +#define K210_RST_SPI2 8 +#define K210_RST_SPI3 9 +#define K210_RST_I2S0 10 +#define K210_RST_I2S1 11 +#define K210_RST_I2S2 12 +#define K210_RST_I2C0 13 +#define K210_RST_I2C1 14 +#define K210_RST_I2C2 15 +#define K210_RST_UART1 16 +#define K210_RST_UART2 17 +#define K210_RST_UART3 18 +#define K210_RST_AES 19 +#define K210_RST_FPIOA 20 +#define K210_RST_TIMER0 21 +#define K210_RST_TIMER1 22 +#define K210_RST_TIMER2 23 +#define K210_RST_WDT0 24 +#define K210_RST_WDT1 25 +#define K210_RST_SHA 26 +#define K210_RST_RTC 29 + +#endif /* RESET_K210_SYSCTL_H */ From patchwork Wed Jan 15 23:20:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239632 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 18:20:47 -0500 Subject: [PATCH v2 09/11] riscv: Add K210 sysctl support In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: <9532cfb9-701c-d4f3-b976-b5c6f6002f8f@gmail.com> This driver does nothing but load its children for the moment. Should it be using regmap? I'm not sure how that fits into everything. Signed-off-by: Sean Anderson --- Changes for v2: Removed spurious references to mach-k210. Don't load pre-relocation. arch/riscv/Kconfig | 11 +++++++ arch/riscv/include/asm/k210_sysctl.h | 43 ++++++++++++++++++++++++++++ arch/riscv/lib/Makefile | 1 + arch/riscv/lib/k210_sysctl.c | 21 ++++++++++++++ board/sipeed/maix/Kconfig | 1 + 5 files changed, 77 insertions(+) create mode 100644 arch/riscv/include/asm/k210_sysctl.h create mode 100644 arch/riscv/lib/k210_sysctl.c diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 4c62b8dd77..677cdf5f24 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -196,6 +196,14 @@ config RISCV_RDTIME standard rdtime instruction. This is the case for S-mode U-Boot, and is useful for processors that support rdtime in M-mode too. +config K210_SYSCTL + bool + select SYSCON + select SPL_SYSCON if SPL + help + The K210 sysctl block holds memory-mapped control and status + registers associated with clocks, resets, power, and dma handshakes. + config SYS_MALLOC_F_LEN default 0x1000 @@ -240,4 +248,7 @@ config STACK_SIZE_SHIFT config SPL_LDSCRIPT default "arch/riscv/cpu/u-boot-spl.lds" +config ARCH_K210 + bool "Support Kendryte K210 SOCs" + endmenu diff --git a/arch/riscv/include/asm/k210_sysctl.h b/arch/riscv/include/asm/k210_sysctl.h new file mode 100644 index 0000000000..94170f4f31 --- /dev/null +++ b/arch/riscv/include/asm/k210_sysctl.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 Sean Anderson + */ + +#ifndef K210_SYSCTL_H +#define K210_SYSCTL_H + +#include + +/* + * sysctl registers + * Taken from kendryte-standalone-sdk/lib/drivers/include/sysctl.h + */ +struct k210_sysctl { + u32 git_id; + u32 clk_freq; + u32 pll0; + u32 pll1; + u32 pll2; + u32 resv5; + u32 pll_lock; + u32 rom_error; + u32 clk_sel[2]; + u32 clk_en_cent; + u32 clk_en_peri; + u32 soft_reset; + u32 peri_reset; + u32 clk_thr[7]; + u32 misc; + u32 peri; + u32 spi_sleep; + u32 reset_status; + u32 dma_sel0; + u32 dma_sel1; + u32 power_sel; + u32 resv28; + u32 resv29; + u32 resv30; + u32 resv31; +} __packed; + +#endif /* K210_SYSCTL_H */ diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index c9179a5ff8..4c31e824d9 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -14,6 +14,7 @@ ifeq ($(CONFIG_$(SPL_)RISCV_MMODE),y) obj-$(CONFIG_SIFIVE_CLINT) += sifive_clint.o obj-$(CONFIG_ANDES_PLIC) += andes_plic.o obj-$(CONFIG_ANDES_PLMT) += andes_plmt.o +obj-$(CONFIG_K210_SYSCTL) += k210_sysctl.o else obj-$(CONFIG_RISCV_RDTIME) += rdtime.o obj-$(CONFIG_SBI_IPI) += sbi_ipi.o diff --git a/arch/riscv/lib/k210_sysctl.c b/arch/riscv/lib/k210_sysctl.c new file mode 100644 index 0000000000..db2e79c974 --- /dev/null +++ b/arch/riscv/lib/k210_sysctl.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Sean Anderson + */ +#include + +#include + +static const struct udevice_id k210_sysctl_ids[] = { + { .compatible = "kendryte,k210-sysctl", }, + { } +}; + +U_BOOT_DRIVER(k210_sysctl) = { + .name = "k210_sysctl", + .id = UCLASS_SYSCON, +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + .bind = dm_scan_fdt_dev, +#endif + .of_match = k210_sysctl_ids, +}; diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig index efeaddf317..9ffd3aa7aa 100644 --- a/board/sipeed/maix/Kconfig +++ b/board/sipeed/maix/Kconfig @@ -36,6 +36,7 @@ config BOARD_SPECIFIC_OPTIONS select ENV_IS_NOWHERE select SYS_RISCV_NOCOUNTER imply SIFIVE_CLINT + imply K210_SYSCTL imply SPI imply DM_GPIO imply CMD_GPIO From patchwork Wed Jan 15 23:24:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239633 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 18:24:41 -0500 Subject: [PATCH v2 10/11] riscv: Add K210 pll support In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: This pll code is primarily based on the code from the kendryte standalone sdk in lib/drivers/sysctl.c. k210_pll_calc_params is roughly analogous to the algorithm used to set the pll frequency, but it has been completely rewritten to be fixed-point based. Signed-off-by: Sean Anderson --- Changes for v2. Rename driver to "k210_clk_pll" Add additional in-line documentation on algorithm and PLLs. Restrict the range of internal VCO and reference frequencies. Don't load driver before relocation. Remove spurious references to mach-k210. board/sipeed/maix/Kconfig | 1 + configs/sipeed_maix_bitm_defconfig | 1 + drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/kendryte/Kconfig | 7 + drivers/clk/kendryte/Makefile | 1 + drivers/clk/kendryte/pll.c | 598 +++++++++++++++++++++++++++++ drivers/clk/kendryte/pll.h | 38 ++ 8 files changed, 648 insertions(+) create mode 100644 drivers/clk/kendryte/Kconfig create mode 100644 drivers/clk/kendryte/Makefile create mode 100644 drivers/clk/kendryte/pll.c create mode 100644 drivers/clk/kendryte/pll.h diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig index 9ffd3aa7aa..10e492a425 100644 --- a/board/sipeed/maix/Kconfig +++ b/board/sipeed/maix/Kconfig @@ -37,6 +37,7 @@ config BOARD_SPECIFIC_OPTIONS select SYS_RISCV_NOCOUNTER imply SIFIVE_CLINT imply K210_SYSCTL + imply CLK_K210 imply SPI imply DM_GPIO imply CMD_GPIO diff --git a/configs/sipeed_maix_bitm_defconfig b/configs/sipeed_maix_bitm_defconfig index ecd69c0873..aeab7db0c3 100644 --- a/configs/sipeed_maix_bitm_defconfig +++ b/configs/sipeed_maix_bitm_defconfig @@ -65,6 +65,7 @@ CONFIG_OF_TRANSLATE=y CONFIG_CLK=y CONFIG_CLK_CCF=y CONFIG_CLK_COMPOSITE_CCF=y +CONFIG_CLK_K210=y CONFIG_CPU=y CONFIG_CPU_RISCV=y CONFIG_MMC=y diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 16d4237f89..af75c7c4cf 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -145,6 +145,7 @@ source "drivers/clk/analogbits/Kconfig" source "drivers/clk/at91/Kconfig" source "drivers/clk/exynos/Kconfig" source "drivers/clk/imx/Kconfig" +source "drivers/clk/kendryte/Kconfig" source "drivers/clk/meson/Kconfig" source "drivers/clk/mvebu/Kconfig" source "drivers/clk/owl/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 06131edb9f..4f3893f6fc 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o obj-$(CONFIG_CLK_BOSTON) += clk_boston.o obj-$(CONFIG_CLK_EXYNOS) += exynos/ obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o +obj-$(CONFIG_CLK_K210) += kendryte/ obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o obj-$(CONFIG_CLK_OWL) += owl/ obj-$(CONFIG_CLK_RENESAS) += renesas/ diff --git a/drivers/clk/kendryte/Kconfig b/drivers/clk/kendryte/Kconfig new file mode 100644 index 0000000000..a178d50f5e --- /dev/null +++ b/drivers/clk/kendryte/Kconfig @@ -0,0 +1,7 @@ +config CLK_K210 + bool "Clock support for Kendryte K210" + depends on K210_SYSCTL + select CLK + select CLK_CCF + help + This enables support clock driver for Kendryte K210 platforms. diff --git a/drivers/clk/kendryte/Makefile b/drivers/clk/kendryte/Makefile new file mode 100644 index 0000000000..c56d93ea1c --- /dev/null +++ b/drivers/clk/kendryte/Makefile @@ -0,0 +1 @@ +obj-y += pll.o diff --git a/drivers/clk/kendryte/pll.c b/drivers/clk/kendryte/pll.c new file mode 100644 index 0000000000..cbb112f5a8 --- /dev/null +++ b/drivers/clk/kendryte/pll.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Sean Anderson + */ +#include "pll.h" + +#define LOG_CATEGORY UCLASS_CLK +#include +/* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */ +#include +#include +#include +#include +#include +#include +#include + +#define CLK_K210_PLL "k210_clk_pll" +#define to_k210_pll(_clk) container_of(_clk, struct k210_pll, clk) + +static int k210_pll_enable(struct clk *clk); +static int k210_pll_disable(struct clk *clk); + +/* + * The logical layout of the PLL is approximately the following: + * + * +-----------+ + * |input clock| + * +-----------+ + * | + * v + * +--+ + * |/r| + * +--+ + * | + * v + * +---------------+ + * |reference clock| + * +---------------+ + * | + * v + * +----------------+ + * |phase comparator|<--+ + * +----------------+ | + * | | + * v | + * +---+ | + * |VCO| | + * +---+ | + * | +--+ | + * +--->|/f|---+ + * | +--+ + * v + * +---+ + * |/od| + * +---+ + * | + * v + * +------+ + * |output| + * +------+ + * + * The k210 PLLs have three factors: r, f, and od. Because of the feedback mode, + * the effect of the division by f is to multiply the input frequency. The + * equation for the output rate is + * rate = (rate_in * f) / (r * od). + * Moving knowns to one side of the equation, we get + * rate / rate_in = f / (r * od) + * Rearranging slightly, + * abs_error = abs((rate / rate_in) - (f / (r * od))). + * To get relative, error, we divide by the expected ratio + * error = abs((rate / rate_in) - (f / (r * od))) / (rate / rate_in). + * Simplifying, + * error = abs(1 - f / (r * od)) / (rate / rate_in) + * error = abs(1 - (f * rate_in) / (r * od * rate)) + * Using the constants ratio = rate / rate_in and inv_ratio = rate_in / rate, + * error = abs((f * inv_ratio) / (r * od) - 1) + * This is the error used in evaluating parameters. + * + * r and od are four bits each, while f is six bits. Because r and od are + * multiplied together, instead of the full 256 values possible if both bits + * were used fully, there are only 97 distinct products. Combined with f, there + * are 6208 theoretical settings for the PLL. However, most of these settings + * can be ruled out immediately because they do not have the correct ratio. + * + * In addition to the constraint of approximating the desired ratio, parameters + * must also keep internal pll frequencies within acceptable ranges. The + * reference clock's minimum and maximum frequencies have a ratio of around 128. + * This leaves fairly substantial room work work with, especially since the only + * affected parameter is r. The VCO's minimum and maximum frequency have a ratio + * of 5, which is considerably more restrictive. + * + * The r and od factors are stored in a table. This is to make it easy to find + * the next-largest product. Some products have multiple factorizations, but + * only when one factor has at least a 2.5x ratio to the factors of the other + * factorization. This is because any smaller ratio would not make a difference + * when ensuring the VCO's frequency is within spec. + * + * Throughout the calculation function, fixed point arithmetic is used. Because + * the range of rate and rate_in may be up to 1.75 GHz, or around 2^30, 64-bit + * 32.32 fixed-point numbers are used to represent ratios. In general, to + * implement division, the numerator is first multiplied by 2^32. This gives a + * result where the whole number part is in the upper 32 bits, and the fraction + * is in the lower 32 bits. + * + * In general, rounding is done to the closest integer. This helps find the best + * approximation for the ratio. Rounding in one direction (e.g down) could cause + * the function to miss a better ratio with one of the parameters increased by + * one. + */ + +/* + * The factors table was generated with the following python code: + * + * def p(x, y): + * return (1.0*x/y > 2.5) or (1.0*y/x > 2.5) + * + * factors = {} + * for i in range(1, 17): + * for j in range(1, 17): + * fs = factors.get(i*j) or [] + * if fs == [] or all([ + * (p(i, x) and p(i, y)) or (p(j, x) and p(j, y)) + * for (x, y) in fs]): + * fs.append((i, j)) + * factors[i*j] = fs + * + * for k, l in sorted(factors.items()): + * for v in l: + * print("PACK(%s, %s)," % v) + */ +#define PACK(r, od) ((((r - 1) & 0xF) << 4) | ((od - 1) & 0xF)) +#define UNPACK_R(val) (((val >> 4) & 0xF) + 1) +#define UNPACK_OD(val) ((val & 0xF) + 1) +static const u8 factors[] = { + PACK(1, 1), + PACK(1, 2), + PACK(1, 3), + PACK(1, 4), + PACK(1, 5), + PACK(1, 6), + PACK(1, 7), + PACK(1, 8), + PACK(1, 9), + PACK(3, 3), + PACK(1, 10), + PACK(1, 11), + PACK(1, 12), + PACK(3, 4), + PACK(1, 13), + PACK(1, 14), + PACK(1, 15), + PACK(3, 5), + PACK(1, 16), + PACK(4, 4), + PACK(2, 9), + PACK(2, 10), + PACK(3, 7), + PACK(2, 11), + PACK(2, 12), + PACK(5, 5), + PACK(2, 13), + PACK(3, 9), + PACK(2, 14), + PACK(2, 15), + PACK(2, 16), + PACK(3, 11), + PACK(5, 7), + PACK(3, 12), + PACK(3, 13), + PACK(4, 10), + PACK(3, 14), + PACK(4, 11), + PACK(3, 15), + PACK(3, 16), + PACK(7, 7), + PACK(5, 10), + PACK(4, 13), + PACK(6, 9), + PACK(5, 11), + PACK(4, 14), + PACK(4, 15), + PACK(7, 9), + PACK(4, 16), + PACK(5, 13), + PACK(6, 11), + PACK(5, 14), + PACK(6, 12), + PACK(5, 15), + PACK(7, 11), + PACK(6, 13), + PACK(5, 16), + PACK(9, 9), + PACK(6, 14), + PACK(8, 11), + PACK(6, 15), + PACK(7, 13), + PACK(6, 16), + PACK(7, 14), + PACK(9, 11), + PACK(10, 10), + PACK(8, 13), + PACK(7, 15), + PACK(9, 12), + PACK(10, 11), + PACK(7, 16), + PACK(9, 13), + PACK(8, 15), + PACK(11, 11), + PACK(9, 14), + PACK(8, 16), + PACK(10, 13), + PACK(11, 12), + PACK(9, 15), + PACK(10, 14), + PACK(11, 13), + PACK(9, 16), + PACK(10, 15), + PACK(11, 14), + PACK(12, 13), + PACK(10, 16), + PACK(11, 15), + PACK(12, 14), + PACK(13, 13), + PACK(11, 16), + PACK(12, 15), + PACK(13, 14), + PACK(12, 16), + PACK(13, 15), + PACK(14, 14), + PACK(13, 16), + PACK(14, 15), + PACK(14, 16), + PACK(15, 15), + PACK(15, 16), + PACK(16, 16), +}; + +struct k210_pll_params { + u8 r; + u8 f; + u8 od; +}; + +static int k210_pll_calc_params(u32 rate, u32 rate_in, + struct k210_pll_params *best) +{ + int i; + s64 error, best_error; + u64 ratio, inv_ratio; /* fixed point 32.32 ratio of the rates */ + u64 max_r; + u64 r, f, od; + + /* + * Can't go over 1.75 GHz or under 855 kHz due to limitations on the + * reference clock frequency. These are not the same limits as below + * because od can reduce the output frequency by 16. + */ + if (rate > 1750000000 || rate < 854493) + return -EINVAL; + + ratio = DIV_ROUND_CLOSEST_ULL((u64)rate << 32, rate_in); + inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate); + /* Can't increase by more than 64 or reduce by more than 256 */ + if (rate > rate_in && ratio > (64ULL << 32)) + return -EINVAL; + else if (rate <= rate_in && inv_ratio > (256ULL << 32)) + return -EINVAL; + + /* + * The reference clock (rate_in / r) must stay between 1.75 GHz and 13 + * MHz. There is no minimum, since the only way to get a higher input + * clock than 26 MHz is to use a clock generated by a PLL. Because PLLs + * cannot output frequencies greater than 1.75 GHz, the minimum would + * never be greater than one. + */ + max_r = DIV_ROUND_DOWN_ULL(rate_in, 13671875); + + /* Variables get immediately incremented, so start at -1th iteration */ + i = -1; + f = 0; + r = 0; + od = 0; + error = best_error = S64_MAX; + /* do-while here so we always try at least one ratio */ + do { + /* + * Whether we swapped r and od while enforcing frequency limits + */ + bool swapped = false; + u64 last_od = od; + u64 last_r = r; + + /* + * Try the next largest value for f (or r and od) and + * recalculate the other parameters based on that + */ + if (rate > rate_in) { + /* + * Skip factors of the same product if we already tried + * out that product + */ + do { + i++; + r = UNPACK_R(factors[i]); + od = UNPACK_OD(factors[i]); + } while (i + 1 < ARRAY_SIZE(factors) + && r * od == last_r * last_od); + + /* Round close */ + f = (r * od * ratio + BIT(31)) >> 32; + if (f > 64) + f = 64; + } else { + u64 tmp = ++f * inv_ratio; + bool round_up = !!(tmp & BIT(31)); + u32 goal = (tmp >> 32) + round_up; + u32 err, last_err; + + /* Get the next r/od pair in factors */ + while (r * od < goal && i + 1 < ARRAY_SIZE(factors)) { + i++; + r = UNPACK_R(factors[i]); + od = UNPACK_OD(factors[i]); + } + + /* + * This is a case of double rounding. If we rounded up + * above, we need to round down (in cases of ties) here. + * This prevents off-by-one errors resulting from + * choosing X+2 over X when X.Y rounds up to X+1 and + * there is no r * od = X+1. For the converse, when X.Y + * is rounded down to X, we should choose X+1 over X-1. + */ + err = abs(r * od - goal); + last_err = abs(last_r * last_od - goal); + if (last_err < err || (round_up && (last_err = err))) { + i--; + r = last_r; + od = last_od; + } + } + + /* + * Enforce limits on internal clock frequencies. If we + * aren't in spec, try swapping r and od. If everything is + * in-spec, calculate the relative error. + */ + while (true) { + /* + * Whether the intermediate frequencies are out-of-spec + */ + bool out_of_spec = false; + + if (r > max_r) { + out_of_spec = true; + } else { + /* + * There is no way to only divide once; we need + * to examine the frequency with and without the + * effect of od. + */ + u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r); + if (vco > 1750000000 || vco < 350000000) + out_of_spec = true; + } + + if (out_of_spec) { + if (!swapped) { + u64 tmp = r; + r = od; + od = tmp; + swapped = true; + continue; + } else { + /* + * Try looking ahead to see if there are + * additional factors for the same + * product. + */ + if (i + 1 < ARRAY_SIZE(factors)) { + u64 new_r, new_od; + + i++; + new_r = UNPACK_R(factors[i]); + new_od = UNPACK_OD(factors[i]); + if (r * od == new_r * new_od) { + r = new_r; + od = new_od; + swapped = false; + continue; + } + i--; + } + break; + } + } + + error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio, r * od); + /* The lower 16 bits are spurious */ + error = abs((error - BIT(32))) >> 16; + + if (error < best_error) { + best->r = r; + best->f = f; + best->od = od; + best_error = error; + } + break; + } + } while (f < 64 && i + 1 < ARRAY_SIZE(factors) && error != 0); + + if (best_error == S64_MAX) + return -EINVAL; + + log_debug("best error %lld\n", best_error); + return 0; +} + +static ulong k210_pll_set_rate(struct clk *clk, ulong rate) +{ + int err; + long long rate_in = clk_get_parent_rate(clk); + struct k210_pll_params params = {}; + struct k210_pll *pll = to_k210_pll(clk); + u32 reg; + + if (rate_in < 0) + return rate_in; + + log_debug("Calculating parameters with rate=%lu and rate_in=%lld\n", + rate, rate_in); + err = k210_pll_calc_params(rate, rate_in, ¶ms); + if (err) + return err; + log_debug("Got r=%u f=%u od=%u\n", params.r, params.f, params.od); + + /* + * Don't use clk_disable as it might not actually disable the pll due to + * refcounting + */ + err = k210_pll_disable(clk); + if (err) + return err; + + reg = readl(pll->reg); + reg &= ~K210_PLL_CLKR + & ~K210_PLL_CLKF + & ~K210_PLL_CLKOD + & ~K210_PLL_BWADJ; + reg |= FIELD_PREP(K210_PLL_CLKR, params.r - 1) + | FIELD_PREP(K210_PLL_CLKF, params.f - 1) + | FIELD_PREP(K210_PLL_CLKOD, params.od - 1) + | FIELD_PREP(K210_PLL_BWADJ, params.f - 1); + writel(reg, pll->reg); + + err = k210_pll_enable(clk); + if (err) + return err; + + return clk_get_rate(clk); +} + +static ulong k210_pll_get_rate(struct clk *clk) +{ + + long long rate_in = clk_get_parent_rate(clk); + struct k210_pll *pll = to_k210_pll(clk); + u64 r, f, od; + u32 reg = readl(pll->reg); + + if (rate_in < 0) + return rate_in; + + if (reg & K210_PLL_BYPASS) + return rate_in; + + r = FIELD_GET(K210_PLL_CLKR, reg) + 1; + f = FIELD_GET(K210_PLL_CLKF, reg) + 1; + od = FIELD_GET(K210_PLL_CLKOD, reg) + 1; + + return DIV_ROUND_DOWN_ULL(((u64)rate_in) * f, r * od); +} + +/* Check if the PLL is locked */ +static int k210_pll_locked(struct k210_pll *pll) +{ + u32 reg = readl(pll->lock); + + return (reg & pll->lock_mask) == pll->lock_mask; +} + +/* + * Wait for the PLL to be locked. If the PLL is not locked, try clearing the + * slip before retrying + */ +static void k210_pll_waitfor_lock(struct k210_pll *pll) +{ + while (!k210_pll_locked(pll)) { + u32 reg = readl(pll->lock); + + reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP); + writel(reg, pll->lock); + udelay(1); + } +} + +/* Adapted from sysctl_pll_enable */ +static int k210_pll_enable(struct clk *clk) +{ + struct k210_pll *pll = to_k210_pll(clk); + u32 reg = readl(pll->reg); + + reg &= ~K210_PLL_BYPASS; + writel(reg, pll->reg); + + reg |= K210_PLL_PWRD; + writel(reg, pll->reg); + + /* Ensure reset is low before asserting it */ + reg &= ~K210_PLL_RESET; + writel(reg, pll->reg); + reg |= K210_PLL_RESET; + writel(reg, pll->reg); + /* FIXME: this doesn't really have to be a whole microsecond */ + udelay(1); + reg &= ~K210_PLL_RESET; + writel(reg, pll->reg); + + k210_pll_waitfor_lock(pll); + return 0; +} + +static int k210_pll_disable(struct clk *clk) +{ + struct k210_pll *pll = to_k210_pll(clk); + u32 reg = readl(pll->reg); + + /* + * Bypassing before powering off is important so child clocks don't stop + * working. This is especially important for pll0, the indirect parent + * of the cpu clock. + */ + reg |= K210_PLL_BYPASS; + writel(reg, pll->reg); + + reg &= ~K210_PLL_PWRD; + writel(reg, pll->reg); + return 0; +} + +const struct clk_ops k210_pll_ops = { + .get_rate = k210_pll_get_rate, + .set_rate = k210_pll_set_rate, + .enable = k210_pll_enable, + .disable = k210_pll_disable, +}; + +struct k210_pll *k210_clk_comp_pll(void __iomem *reg, void __iomem *lock, + u8 shift, u8 width) +{ + struct k210_pll *pll; + + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return pll; + pll->reg = reg; + pll->lock = lock; + pll->shift = shift; + pll->lock_mask = GENMASK(shift + width, shift); + return pll; +} + +struct clk *k210_clk_pll(const char *name, const char *parent_name, + void __iomem *reg, void __iomem *lock, u8 shift, + u8 width) +{ + int err; + struct k210_pll *pll; + + pll = k210_clk_comp_pll(reg, lock, shift, width); + if (!pll) + return ERR_PTR(-ENOMEM); + + err = clk_register(&pll->clk, CLK_K210_PLL, name, parent_name); + if (err) { + kfree(pll); + return ERR_PTR(err); + } + return &pll->clk; +} + +U_BOOT_DRIVER(k210_pll) = { + .name = CLK_K210_PLL, + .id = UCLASS_CLK, + .ops = &k210_pll_ops, +}; diff --git a/drivers/clk/kendryte/pll.h b/drivers/clk/kendryte/pll.h new file mode 100644 index 0000000000..6a8734a295 --- /dev/null +++ b/drivers/clk/kendryte/pll.h @@ -0,0 +1,38 @@ +#ifndef K210_PLL_H +#define K210_PLL_H + +#include + +#define K210_PLL_CLKR GENMASK(3, 0) +#define K210_PLL_CLKF GENMASK(9, 4) +#define K210_PLL_CLKOD GENMASK(13, 10) +#define K210_PLL_BWADJ GENMASK(19, 14) +#define K210_PLL_RESET BIT(20) +#define K210_PLL_PWRD BIT(21) +#define K210_PLL_INTFB BIT(22) +#define K210_PLL_BYPASS BIT(23) +#define K210_PLL_TEST BIT(24) +#define K210_PLL_EN BIT(25) +#define K210_PLL_TEST_EN BIT(26) + +#define K210_PLL_LOCK 0 +#define K210_PLL_CLEAR_SLIP 2 +#define K210_PLL_TEST_OUT 3 + +struct k210_pll { + struct clk clk; + void __iomem *reg; /* Base PLL register */ + void __iomem *lock; /* Common PLL lock register */ + u8 shift; /* Offset of bits in lock register */ + u8 lock_mask; /* Mask of lock bits to test against, pre-shifted */ +}; + +extern const struct clk_ops k210_pll_ops; + +struct k210_pll *k210_clk_comp_pll(void __iomem *reg, void __iomem *lock, + u8 shift, u8 width); +struct clk *k210_clk_pll(const char *name, const char *parent_name, + void __iomem *reg, void __iomem *lock, u8 shift, + u8 width); + +#endif /* K210_PLL_H */ From patchwork Wed Jan 15 23:26:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 239634 List-Id: U-Boot discussion From: seanga2 at gmail.com (Sean Anderson) Date: Wed, 15 Jan 2020 18:26:53 -0500 Subject: [PATCH v2 11/11] riscv: Add K210 clock support In-Reply-To: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> References: <3411d84b-5a56-ae4e-1deb-085ef1a4971d@gmail.com> Message-ID: 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 --- Changes for v2: Add clk.o to obj-y. Don't probe before relocation. arch/riscv/Kconfig | 3 - board/sipeed/maix/Kconfig | 3 + drivers/clk/kendryte/Makefile | 2 +- drivers/clk/kendryte/clk.c | 390 ++++++++++++++++++++++++++++++++++ drivers/clk/kendryte/clk.h | 27 +++ 5 files changed, 421 insertions(+), 4 deletions(-) create mode 100644 drivers/clk/kendryte/clk.c create mode 100644 drivers/clk/kendryte/clk.h diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 677cdf5f24..6b05ef72d0 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -248,7 +248,4 @@ config STACK_SIZE_SHIFT config SPL_LDSCRIPT default "arch/riscv/cpu/u-boot-spl.lds" -config ARCH_K210 - bool "Support Kendryte K210 SOCs" - endmenu diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig index 10e492a425..c456c47fb2 100644 --- a/board/sipeed/maix/Kconfig +++ b/board/sipeed/maix/Kconfig @@ -27,6 +27,9 @@ config NR_CPUS config NR_DRAM_BANKS default 2 +config SYS_MALLOC_F_LEN + default 0x8000 + config BOARD_SPECIFIC_OPTIONS def_bool y select GENERIC_RISCV 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..c6405a329c --- /dev/null +++ b/drivers/clk/kendryte/clk.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Sean Anderson + */ +#include "clk.h" + +#include +#include +#include +#include +#include +#include + +#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; + struct k210_sysctl *sysctl; + + sysctl = dev_read_addr_ptr(dev_get_parent(dev)); + if (!sysctl) + return -EINVAL; + + err = clk_get_by_index(dev, 0, &in0_clk); + if (err) + goto cleanup_sysctl; + in0 = in0_clk.dev->name; + generic_sels[0] = in0; + pll2_sels[0] = in0; + + /* PLLs */ + clk_dm(K210_CLK_PLL0, k210_clk_pll("pll0", in0, &sysctl->pll0, + &sysctl->pll_lock, 0, 2)); + clk_dm(K210_CLK_PLL1, k210_clk_pll("pll1", in0, &sysctl->pll1, + &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), + &sysctl->pll2, 26, 2); + pll = k210_clk_comp_pll(&sysctl->pll2, &sysctl->pll_lock, 16, 1); + if (!mux || !pll) { + kfree(mux); + kfree(pll); + } else { + clk_dm(K210_CLK_PLL0, + 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(&sysctl->clk_sel[0], 1, 2, + CLK_DIVIDER_POWER_OF_TWO); + /* ACLK has no gate */ + mux = k210_clk_comp_mux(aclk_sels, ARRAY_SIZE(generic_sels), + &sysctl->clk_sel[0], 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(&sysctl->clk_sel[0], 1, 2); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 9); + mux = k210_clk_comp_mux(generic_sels, ARRAY_SIZE(generic_sels), + &sysctl->clk_sel[0], 12, 1); + clk_dm(K210_CLK_SPI3, k210_clk_comp("spi3", div, gate, mux)); + + div = k210_clk_comp_div(&sysctl->clk_thr[2], 8, 0); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 21); + mux = k210_clk_comp_mux(generic_sels, ARRAY_SIZE(generic_sels), + &sysctl->clk_sel[0], 12, 1); + clk_dm(K210_CLK_TIMER0, k210_clk_comp("timer0", div, gate, mux)); + + div = k210_clk_comp_div(&sysctl->clk_thr[2], 8, 8); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 22); + mux = k210_clk_comp_mux(generic_sels, ARRAY_SIZE(generic_sels), + &sysctl->clk_sel[0], 12, 1); + clk_dm(K210_CLK_TIMER1, k210_clk_comp("timer1", div, gate, mux)); + + div = k210_clk_comp_div(&sysctl->clk_thr[2], 8, 16); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 23); + mux = k210_clk_comp_mux(generic_sels, ARRAY_SIZE(generic_sels), + &sysctl->clk_sel[0], 12, 1); + clk_dm(K210_CLK_TIMER2, k210_clk_comp("timer2", div, gate, mux)); + + + /* Dividing clocks, no mux */ + div = k210_clk_comp_div(&sysctl->clk_thr[0], 0, 4); + gate = k210_clk_comp_gate(&sysctl->clk_en_cent, 1); + clk_dm(K210_CLK_SRAM0, k210_clk_comp_nomux("sram0", "aclk", div, gate)); + + div = k210_clk_comp_div( &sysctl->clk_thr[0], 4, 4); + gate = k210_clk_comp_gate( &sysctl->clk_en_cent, 2); + clk_dm(K210_CLK_SRAM1, k210_clk_comp_nomux("sram1", "aclk", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[0], 16, 4); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 0); + clk_dm(K210_CLK_ROM, k210_clk_comp_nomux("rom", "aclk", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[0], 12, 4); + gate = k210_clk_comp_gate(&sysctl->clk_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. + */ + div = k210_clk_comp_div(&sysctl->clk_sel[0], 3, 3); + gate = k210_clk_comp_gate(&sysctl->clk_en_cent, 3); + clk_dm(K210_CLK_APB0, k210_clk_comp_nomux("apb0", "aclk", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_sel[0], 6, 3); + gate = k210_clk_comp_gate(&sysctl->clk_en_cent, 4); + clk_dm(K210_CLK_APB1, k210_clk_comp_nomux("apb1", "aclk", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_sel[0], 9, 3); + gate = k210_clk_comp_gate(&sysctl->clk_en_cent, 5); + clk_dm(K210_CLK_APB1, k210_clk_comp_nomux("apb2", "aclk", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[0], 8, 4); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 2); + clk_dm(K210_CLK_APB1, k210_clk_comp_nomux("ai", "pll1", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[3], 0, 16); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 10); + clk_dm(K210_CLK_I2S0, + k210_clk_comp_nomux("i2s0", "pll2_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[3], 16, 16); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 11); + clk_dm(K210_CLK_I2S1, + k210_clk_comp_nomux("i2s1", "pll2_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[4], 0, 16); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 12); + clk_dm(K210_CLK_I2S2, + k210_clk_comp_nomux("i2s2", "pll2_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[6], 0, 8); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 24); + clk_dm(K210_CLK_WDT0, + k210_clk_comp_nomux("wdt0", "in0_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[6], 8, 8); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 25); + clk_dm(K210_CLK_WDT1, + k210_clk_comp_nomux("wdt1", "in0_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[1], 0, 8); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 6); + clk_dm(K210_CLK_SPI0, + k210_clk_comp_nomux("spi0", "pll0_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[1], 8, 8); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 7); + clk_dm(K210_CLK_SPI1, + k210_clk_comp_nomux("spi1", "pll0_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[1], 16, 8); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 8); + clk_dm(K210_CLK_SPI2, + k210_clk_comp_nomux("spi2", "pll0_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[5], 8, 8); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 13); + clk_dm(K210_CLK_SPI2, + k210_clk_comp_nomux("i2c0", "pll0_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[5], 16, 8); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 14); + clk_dm(K210_CLK_SPI2, + k210_clk_comp_nomux("i2c1", "pll0_half", div, gate)); + + div = k210_clk_comp_div(&sysctl->clk_thr[5], 24, 8); + gate = k210_clk_comp_gate(&sysctl->clk_en_peri, 15); + clk_dm(K210_CLK_SPI2, + k210_clk_comp_nomux("i2c2", "pll0_half", div, gate)); + + /* Gated clocks */ + clk_dm(K210_CLK_CPU, + k210_clk_gate("cpu", "aclk", &sysctl->clk_en_cent, 0)); + clk_dm(K210_CLK_DMA, + k210_clk_gate("dma", "aclk", &sysctl->clk_en_peri, 1)); + clk_dm(K210_CLK_FFT, + k210_clk_gate("fft", "aclk", &sysctl->clk_en_peri, 4)); + clk_dm(K210_CLK_GPIO, + k210_clk_gate("gpio", "apb0", &sysctl->clk_en_peri, 5)); + clk_dm(K210_CLK_UART1, + k210_clk_gate("uart1", "apb0", &sysctl->clk_en_peri, 16)); + clk_dm(K210_CLK_UART2, + k210_clk_gate("uart2", "apb0", &sysctl->clk_en_peri, 17)); + clk_dm(K210_CLK_UART3, + k210_clk_gate("uart3", "apb0", &sysctl->clk_en_peri, 18)); + clk_dm(K210_CLK_FPIOA, + k210_clk_gate("fpioa", "apb0", &sysctl->clk_en_peri, 20)); + clk_dm(K210_CLK_SHA, + k210_clk_gate("sha", "apb0", &sysctl->clk_en_peri, 26)); + clk_dm(K210_CLK_AES, + k210_clk_gate("aes", "apb1", &sysctl->clk_en_peri, 19)); + clk_dm(K210_CLK_OTP, + k210_clk_gate("otp", "apb1", &sysctl->clk_en_peri, 27)); + clk_dm(K210_CLK_RTC, + k210_clk_gate("rtc", in0, &sysctl->clk_en_peri, 29)); + +cleanup_sysctl: + unmap_sysmem(sysctl); + 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 + */ + +#ifndef K210_CLK_H +#define K210_CLK_H + +#define LOG_CATEGORY UCLASS_CLK +#include +#include + +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 */