Merge branch '2020-07-01-kconfig-etc-updates' into next
authorTom Rini <trini@konsulko.com>
Sun, 5 Jul 2020 22:03:32 +0000 (18:03 -0400)
committerTom Rini <trini@konsulko.com>
Sun, 5 Jul 2020 22:03:32 +0000 (18:03 -0400)
- Resync Kconfiglib with the v14.1.0 release.
- Re-sync our <linux/compiler*h> files with v5.7-rc5 from upstream.
- Fully resync checkpatch.pl with v5.7 release.

To safely to all of the above, we have a few bugfixes about functions
that need a 'static inline' but weren't.  We also stop setting
CROSS_COMPILE in arch/*/config.mk.  Finally, with the above changes
boards can now opt-in to optimizing inlining and we do this for the
socfpga stratix10 platform for space savings.

72 files changed:
MAINTAINERS
arch/riscv/Kconfig
arch/riscv/cpu/cpu.c
arch/riscv/cpu/start.S
arch/riscv/dts/Makefile
arch/riscv/dts/hifive-unleashed-a00-u-boot.dtsi
arch/riscv/dts/k210-maix-bit.dts [new file with mode: 0644]
arch/riscv/dts/k210.dtsi [new file with mode: 0644]
arch/riscv/include/asm/csr.h
arch/riscv/include/asm/global_data.h
arch/riscv/include/asm/smp.h
arch/riscv/lib/andes_plic.c
arch/riscv/lib/reset.c
arch/riscv/lib/sbi_ipi.c
arch/riscv/lib/sifive_clint.c
arch/riscv/lib/smp.c
arch/sandbox/dts/test.dts
arch/sandbox/include/asm/clk.h
board/sipeed/maix/Kconfig [new file with mode: 0644]
board/sipeed/maix/MAINTAINERS [new file with mode: 0644]
board/sipeed/maix/Makefile [new file with mode: 0644]
board/sipeed/maix/maix.c [new file with mode: 0644]
common/spl/spl_opensbi.c
configs/sandbox_defconfig
configs/sipeed_maix_bitm_defconfig [new file with mode: 0644]
doc/board/index.rst
doc/board/sipeed/index.rst [new file with mode: 0644]
doc/board/sipeed/maix.rst [new file with mode: 0644]
doc/device-tree-bindings/bus/simple-pm-bus.txt [new file with mode: 0644]
doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt [new file with mode: 0644]
doc/device-tree-bindings/reset/syscon-reset.txt [new file with mode: 0644]
doc/imx/clk/ccf.txt
drivers/clk/Kconfig
drivers/clk/Makefile
drivers/clk/clk-composite.c
drivers/clk/clk-divider.c
drivers/clk/clk-fixed-factor.c
drivers/clk/clk-gate.c
drivers/clk/clk-mux.c
drivers/clk/clk-uclass.c
drivers/clk/imx/clk-gate2.c
drivers/clk/imx/clk-imx8mp.c
drivers/clk/kendryte/Kconfig [new file with mode: 0644]
drivers/clk/kendryte/Makefile [new file with mode: 0644]
drivers/clk/kendryte/bypass.c [new file with mode: 0644]
drivers/clk/kendryte/clk.c [new file with mode: 0644]
drivers/clk/kendryte/pll.c [new file with mode: 0644]
drivers/core/Kconfig
drivers/core/Makefile
drivers/core/read.c
drivers/core/simple-pm-bus.c [new file with mode: 0644]
drivers/cpu/cpu-uclass.c
drivers/cpu/riscv_cpu.c
drivers/pinctrl/broadcom/pinctrl-bcm283x.c
drivers/pinctrl/mediatek/pinctrl-mtk-common.c
drivers/reset/Kconfig
drivers/reset/Makefile
drivers/reset/reset-syscon.c [new file with mode: 0644]
include/configs/sipeed-maix.h [new file with mode: 0644]
include/dm/read.h
include/dt-bindings/clock/k210-sysctl.h [new file with mode: 0644]
include/dt-bindings/mfd/k210-sysctl.h [new file with mode: 0644]
include/dt-bindings/reset/k210-sysctl.h [new file with mode: 0644]
include/kendryte/bypass.h [new file with mode: 0644]
include/kendryte/clk.h [new file with mode: 0644]
include/kendryte/pll.h [new file with mode: 0644]
include/test/export.h [new file with mode: 0644]
lib/hashtable.c
test/dm/Makefile
test/dm/k210_pll.c [new file with mode: 0644]
test/dm/simple-pm-bus.c [new file with mode: 0644]
test/dm/syscon-reset.c [new file with mode: 0644]

index 0623da0..9d2d3a7 100644 (file)
@@ -874,6 +874,13 @@ F: arch/riscv/
 F:     cmd/riscv/
 F:     tools/prelink-riscv.c
 
+RISC-V KENDRYTE
+M:     Sean Anderson <seanga2@gmail.com>
+S:     Maintained
+F:     doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
+F:     drivers/clk/kendryte/
+F:     include/kendryte/
+
 RNG
 M:     Sughosh Ganu <sughosh.ganu@linaro.org>
 R:     Heinrich Schuchardt <xypron.glpk@gmx.de>
index d9854f5..5d01f40 100644 (file)
@@ -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"
@@ -269,6 +273,16 @@ config XIP
 config SHOW_REGS
        bool "Show registers on unhandled exception"
 
+config RISCV_PRIV_1_9
+       bool "Use version 1.9 of the RISC-V priviledged specification"
+       help
+         Older versions of the RISC-V priviledged specification had
+         separate counter enable CSRs for each privilege mode. Writing
+         to the unified mcounteren CSR on a processor implementing the
+         old specification will result in an illegal instruction
+         exception. In addition to counter CSR changes, the way virtual
+         memory is configured was also changed.
+
 config STACK_SIZE_SHIFT
        int
        default 14
index 5804aa8..bbd6c15 100644 (file)
@@ -91,13 +91,28 @@ int arch_cpu_init_dm(void)
                 * Enable perf counters for cycle, time,
                 * and instret counters only
                 */
+#ifdef CONFIG_RISCV_PRIV_1_9
+               csr_write(CSR_MSCOUNTEREN, GENMASK(2, 0));
+               csr_write(CSR_MUCOUNTEREN, GENMASK(2, 0));
+#else
                csr_write(CSR_MCOUNTEREN, GENMASK(2, 0));
+#endif
 
                /* Disable paging */
                if (supports_extension('s'))
+#ifdef CONFIG_RISCV_PRIV_1_9
+                       csr_read_clear(CSR_MSTATUS, SR_VM);
+#else
                        csr_write(CSR_SATP, 0);
+#endif
        }
 
+#ifdef CONFIG_SMP
+       ret = riscv_init_ipi();
+       if (ret)
+               return ret;
+#endif
+
        return 0;
 }
 
index 5f1c220..f408e41 100644 (file)
@@ -65,6 +65,8 @@ _start:
 #else
        li      t0, SIE_SSIE
 #endif
+       /* Clear any pending IPIs */
+       csrc    MODE_PREFIX(ip), t0
        csrs    MODE_PREFIX(ie), t0
 #endif
 
index 4f30e69..3a6f96c 100644 (file)
@@ -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)
 
index 3038064..e037150 100644 (file)
@@ -8,6 +8,10 @@
 
 / {
        aliases {
+               cpu1 = &cpu1;
+               cpu2 = &cpu2;
+               cpu3 = &cpu3;
+               cpu4 = &cpu4;
                spi0 = &qspi0;
                spi2 = &qspi2;
        };
diff --git a/arch/riscv/dts/k210-maix-bit.dts b/arch/riscv/dts/k210-maix-bit.dts
new file mode 100644 (file)
index 0000000..5b32c5f
--- /dev/null
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+/dts-v1/;
+
+#include "k210.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+       model = "Sipeed Maix Bit 2.0";
+       compatible = "sipeed,maix-bitm", "sipeed,maix-bit", "kendryte,k210";
+
+       chosen {
+               stdout-path = "serial0:115200";
+       };
+
+       sound {
+               compatible = "simple-audio-card";
+               simple-audio-card,format = "i2s";
+               status = "disabled";
+
+               simple-audio-card,cpu {
+                       sound-dai = <&i2s0 0>;
+               };
+
+               simple-audio-card,codec {
+                       sound-dai = <&mic>;
+               };
+       };
+
+       mic: mic {
+               #sound-dai-cells = <0>;
+               compatible = "memsensing,msm61s4030h0";
+               status = "disabled";
+       };
+};
+
+&uarths0 {
+       status = "okay";
+};
+
+&i2s0 {
+       #sound-dai-cells = <1>;
+};
diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi
new file mode 100644 (file)
index 0000000..2546c7d
--- /dev/null
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <dt-bindings/clock/k210-sysctl.h>
+#include <dt-bindings/mfd/k210-sysctl.h>
+#include <dt-bindings/reset/k210-sysctl.h>
+
+/ {
+       /*
+        * 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 {
+               dma0 = &dmac0;
+               gpio0 = &gpio0;
+               gpio1 = &gpio1_0;
+               i2c0 = &i2c0;
+               i2c1 = &i2c1;
+               i2c2 = &i2c2;
+               pinctrl0 = &fpioa;
+               serial0 = &uarths0;
+               serial1 = &uart1;
+               serial2 = &uart2;
+               serial3 = &uart3;
+               spi0 = &spi0;
+               spi1 = &spi1;
+               spi2 = &spi2;
+               spi3 = &spi3;
+               timer0 = &timer0;
+               timer1 = &timer1;
+               timer2 = &timer2;
+       };
+
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               timebase-frequency = <7800000>;
+               cpu0: cpu@0 {
+                       device_type = "cpu";
+                       compatible = "kendryte,k210", "sifive,rocket0", "riscv";
+                       reg = <0>;
+                       riscv,isa = "rv64imafdgc";
+                       mmu-type = "sv39";
+                       i-cache-block-size = <64>;
+                       i-cache-size = <0x8000>;
+                       d-cache-block-size = <64>;
+                       d-cache-size = <0x8000>;
+                       clocks = <&sysclk K210_CLK_CPU>;
+                       cpu0_intc: interrupt-controller {
+                               #interrupt-cells = <1>;
+                               interrupt-controller;
+                               compatible = "riscv,cpu-intc";
+                       };
+               };
+               cpu1: cpu@1 {
+                       device_type = "cpu";
+                       compatible = "kendryte,k210", "sifive,rocket0", "riscv";
+                       reg = <1>;
+                       riscv,isa = "rv64imafdgc";
+                       mmu-type = "sv39";
+                       i-cache-block-size = <64>;
+                       i-cache-size = <0x8000>;
+                       d-cache-block-size = <64>;
+                       d-cache-size = <0x8000>;
+                       clocks = <&sysclk K210_CLK_CPU>;
+                       cpu1_intc: interrupt-controller {
+                               #interrupt-cells = <1>;
+                               interrupt-controller;
+                               compatible = "riscv,cpu-intc";
+                       };
+               };
+       };
+
+       sram: memory@80000000 {
+               device_type = "memory";
+               compatible = "kendryte,k210-sram";
+               reg = <0x80000000 0x400000>,
+                     <0x80400000 0x200000>,
+                     <0x80600000 0x200000>;
+               reg-names = "sram0", "sram1", "airam";
+               clocks = <&sysclk K210_CLK_SRAM0>,
+                        <&sysclk K210_CLK_SRAM1>,
+                        <&sysclk K210_CLK_PLL1>;
+               clock-names = "sram0", "sram1", "airam";
+       };
+
+       reserved-memory {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               ai_reserved: ai@80600000 {
+                       reg = <0x80600000 0x200000>;
+                       reusable;
+               };
+       };
+
+       clocks {
+               in0: osc {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <26000000>;
+               };
+       };
+
+       soc {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               compatible = "kendryte,k210-soc", "simple-bus";
+               ranges;
+               interrupt-parent = <&plic0>;
+
+               debug0: debug@0 {
+                       compatible = "kendryte,k210-debug", "riscv,debug";
+                       reg = <0x0 0x1000>;
+               };
+
+               rom0: nvmem@1000 {
+                       reg = <0x1000 0x1000>;
+                       read-only;
+               };
+
+               clint0: interrupt-controller@2000000 {
+                       #interrupt-cells = <1>;
+                       compatible = "kendryte,k210-clint", "riscv,clint0";
+                       reg = <0x2000000 0xC000>;
+                       interrupt-controller;
+                       interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>,
+                                             <&cpu1_intc 3>, <&cpu1_intc 7>;
+                       clocks = <&sysclk K210_CLK_CPU>;
+               };
+
+               plic0: interrupt-controller@C000000 {
+                       #interrupt-cells = <1>;
+                       compatible = "kendryte,k210-plic", "riscv,plic0";
+                       reg = <0xC000000 0x4000000>;
+                       interrupt-controller;
+                       interrupts-extended = <&cpu0_intc 9>, <&cpu0_intc 11>,
+                                             <&cpu1_intc 9>, <&cpu1_intc 11>;
+                       riscv,ndev = <65>;
+                       riscv,max-priority = <7>;
+               };
+
+               uarths0: serial@38000000 {
+                       compatible = "kendryte,k210-uarths", "sifive,uart0";
+                       reg = <0x38000000 0x1000>;
+                       interrupts = <33>;
+                       clocks = <&sysclk K210_CLK_CPU>;
+                       status = "disabled";
+               };
+
+               gpio0: gpio-controller@38001000 {
+                       #interrupt-cells = <2>;
+                       #gpio-cells = <2>;
+                       compatible = "kendryte,k210-gpiohs", "sifive,gpio0";
+                       reg = <0x38001000 0x1000>;
+                       interrupt-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>;
+                       gpio-controller;
+                       ngpios = <32>;
+                       status = "disabled";
+               };
+
+               kpu0: kpu@40800000 {
+                       compatible = "kendryte,k210-kpu";
+                       reg = <0x40800000 0xc00000>;
+                       interrupts = <25>;
+                       clocks = <&sysclk K210_CLK_AI>;
+                       memory-region = <&ai_reserved>;
+                       status = "disabled";
+               };
+
+               fft0: fft@42000000 {
+                       compatible = "kendryte,k210-fft";
+                       reg = <0x42000000 0x400000>;
+                       interrupts = <26>;
+                       clocks = <&sysclk K210_CLK_FFT>;
+                       resets = <&sysrst K210_RST_FFT>;
+                       status = "disabled";
+               };
+
+               dmac0: dma-controller@50000000 {
+                       compatible = "kendryte,k210-dmac", "snps,axi-dma-1.01a";
+                       reg = <0x50000000 0x1000>;
+                       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";
+               };
+
+               apb0: bus@50200000 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "kendryte,k210-apb", "simple-pm-bus";
+                       ranges;
+                       clocks = <&sysclk K210_CLK_APB0>;
+
+                       gpio1: gpio-controller@50200000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "kendryte,k210-gpio",
+                                            "snps,dw-apb-gpio";
+                               reg = <0x50200000 0x80>;
+                               clocks = <&sysclk K210_CLK_GPIO>;
+                               resets = <&sysrst K210_RST_GPIO>;
+                               status = "disabled";
+
+                               gpio1_0: gpio1@0 {
+                                       #gpio-cells = <2>;
+                                       #interrupt-cells = <2>;
+                                       compatible = "snps,dw-apb-gpio-port";
+                                       reg = <0>;
+                                       interrupt-controller;
+                                       interrupts = <23>;
+                                       gpio-controller;
+                                       snps,nr-gpios = <8>;
+                               };
+                       };
+
+                       uart1: serial@50210000 {
+                               compatible = "kendryte,k210-uart",
+                                            "snps,dw-apb-uart";
+                               reg = <0x50210000 0x100>;
+                               interrupts = <11>;
+                               clocks = <&sysclk K210_CLK_UART1>;
+                               resets = <&sysrst K210_RST_UART1>;
+                               reg-io-width = <4>;
+                               reg-shift = <2>;
+                               dcd-override;
+                               dsr-override;
+                               cts-override;
+                               ri-override;
+                               status = "disabled";
+                       };
+
+                       uart2: serial@50220000 {
+                               compatible = "kendryte,k210-uart",
+                                            "snps,dw-apb-uart";
+                               reg = <0x50220000 0x100>;
+                               interrupts = <12>;
+                               clocks = <&sysclk K210_CLK_UART2>;
+                               resets = <&sysrst K210_RST_UART2>;
+                               reg-io-width = <4>;
+                               reg-shift = <2>;
+                               dcd-override;
+                               dsr-override;
+                               cts-override;
+                               ri-override;
+                               status = "disabled";
+                       };
+
+                       uart3: serial@50230000 {
+                               compatible = "kendryte,k210-uart",
+                                            "snps,dw-apb-uart";
+                               reg = <0x50230000 0x100>;
+                               interrupts = <13>;
+                               clocks = <&sysclk K210_CLK_UART3>;
+                               resets = <&sysrst K210_RST_UART3>;
+                               reg-io-width = <4>;
+                               reg-shift = <2>;
+                               dcd-override;
+                               dsr-override;
+                               cts-override;
+                               ri-override;
+                               status = "disabled";
+                       };
+
+                       spi2: spi@50240000 {
+                               compatible = "kendryte,k120-spislave",
+                                            "snps,dw-apb-ssi";
+                               spi-slave;
+                               reg = <0x50240000 0x100>;
+                               interrupts = <2>;
+                               clocks = <&sysclk K210_CLK_SPI2>;
+                               resets = <&sysrst K210_RST_SPI2>;
+                               spi-max-frequency = <25000000>;
+                               status = "disabled";
+                       };
+
+                       i2s0: i2s@50250000 {
+                               compatible = "kendryte,k210-i2s",
+                                            "snps,designware-i2s";
+                               reg = <0x50250000 0x200>;
+                               interrupts = <5>;
+                               clocks = <&sysclk K210_CLK_I2S0>;
+                               clock-names = "i2sclk";
+                               resets = <&sysrst K210_RST_I2S0>;
+                               status = "disabled";
+                       };
+
+                       apu0: sound@520250200 {
+                               compatible = "kendryte,k210-apu";
+                               reg = <0x50250200 0x200>;
+                               status = "disabled";
+                       };
+
+                       i2s1: i2s@50260000 {
+                               compatible = "kendryte,k210-i2s",
+                                            "snps,designware-i2s";
+                               reg = <0x50260000 0x200>;
+                               interrupts = <6>;
+                               clocks = <&sysclk K210_CLK_I2S1>;
+                               clock-names = "i2sclk";
+                               resets = <&sysrst K210_RST_I2S1>;
+                               status = "disabled";
+                       };
+
+                       i2s2: i2s@50270000 {
+                               compatible = "kendryte,k210-i2s",
+                                            "snps,designware-i2s";
+                               reg = <0x50270000 0x200>;
+                               interrupts = <7>;
+                               clocks = <&sysclk K210_CLK_I2S2>;
+                               clock-names = "i2sclk";
+                               resets = <&sysrst K210_RST_I2S2>;
+                               status = "disabled";
+                       };
+
+                       i2c0: i2c@50280000 {
+                               compatible = "kendryte,k210-i2c",
+                                            "snps,designware-i2c";
+                               reg = <0x50280000 0x100>;
+                               interrupts = <8>;
+                               clocks = <&sysclk K210_CLK_I2C0>;
+                               resets = <&sysrst K210_RST_I2C0>;
+                               status = "disabled";
+                       };
+
+                       i2c1: i2c@50290000 {
+                               compatible = "kendryte,k210-i2c",
+                                            "snps,designware-i2c";
+                               reg = <0x50290000 0x100>;
+                               interrupts = <9>;
+                               clocks = <&sysclk K210_CLK_I2C1>;
+                               resets = <&sysrst K210_RST_I2C1>;
+                               status = "disabled";
+                       };
+
+                       i2c2: i2c@502A0000 {
+                               compatible = "kendryte,k210-i2c",
+                                            "snps,designware-i2c";
+                               reg = <0x502A0000 0x100>;
+                               interrupts = <10>;
+                               clocks = <&sysclk K210_CLK_I2C2>;
+                               resets = <&sysrst K210_RST_I2C2>;
+                               status = "disabled";
+                       };
+
+                       fpioa: pinmux@502B0000 {
+                               compatible = "kendryte,k210-fpioa";
+                               reg = <0x502B0000 0x100>;
+                               clocks = <&sysclk K210_CLK_FPIOA>;
+                               resets = <&sysrst K210_RST_FPIOA>;
+                               status = "disabled";
+                       };
+
+                       sha256: sha256@502C0000 {
+                               compatible = "kendryte,k210-sha256";
+                               reg = <0x502C0000 0x100>;
+                               clocks = <&sysclk K210_CLK_SHA>;
+                               resets = <&sysrst K210_RST_SHA>;
+                               status = "disabled";
+                       };
+
+                       timer0: timer@502D0000 {
+                               compatible = "kendryte,k210-timer",
+                                            "snps,dw-apb-timer";
+                               reg = <0x502D0000 0x100>;
+                               interrupts = <14 15>;
+                               clocks = <&sysclk K210_CLK_TIMER0>;
+                               clock-names = "timer";
+                               resets = <&sysrst K210_RST_TIMER0>;
+                               status = "disabled";
+                       };
+
+                       timer1: timer@502E0000 {
+                               compatible = "kendryte,k210-timer",
+                                            "snps,dw-apb-timer";
+                               reg = <0x502E0000 0x100>;
+                               interrupts = <16 17>;
+                               clocks = <&sysclk K210_CLK_TIMER1>;
+                               clock-names = "timer";
+                               resets = <&sysrst K210_RST_TIMER1>;
+                               status = "disabled";
+                       };
+
+                       timer2: timer@502F0000 {
+                               compatible = "kendryte,k210-timer",
+                                            "snps,dw-apb-timer";
+                               reg = <0x502F0000 0x100>;
+                               interrupts = <18 19>;
+                               clocks = <&sysclk K210_CLK_TIMER2>;
+                               clock-names = "timer";
+                               resets = <&sysrst K210_RST_TIMER2>;
+                               status = "disabled";
+                       };
+               };
+
+               apb1: bus@50400000 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "kendryte,k210-apb", "simple-pm-bus";
+                       ranges;
+                       clocks = <&sysclk K210_CLK_APB1>;
+
+                       wdt0: watchdog@50400000 {
+                               compatible = "kendryte,k210-wdt", "snps,dw-wdt";
+                               reg = <0x50400000 0x100>;
+                               interrupts = <21>;
+                               clocks = <&sysclk K210_CLK_WDT0>;
+                               resets = <&sysrst K210_RST_WDT0>;
+                               status = "disabled";
+                       };
+
+                       wdt1: watchdog@50410000 {
+                               compatible = "kendryte,k210-wdt", "snps,dw-wdt";
+                               reg = <0x50410000 0x100>;
+                               interrupts = <22>;
+                               clocks = <&sysclk K210_CLK_WDT1>;
+                               resets = <&sysrst K210_RST_WDT1>;
+                               status = "disabled";
+                       };
+
+                       otp0: nvmem@50420000 {
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               compatible = "kendryte,k210-otp";
+                               reg = <0x50420000 0x100>,
+                                     <0x88000000 0x20000>;
+                               reg-names = "reg", "mem";
+                               clocks = <&sysclk K210_CLK_ROM>;
+                               resets = <&sysrst K210_RST_ROM>;
+                               read-only;
+                               status = "disabled";
+
+                               /* Bootloader */
+                               firmware@00000 {
+                                       reg = <0x00000 0xC200>;
+                               };
+
+                               /*
+                                * config string as described in RISC-V
+                                * privileged spec 1.9
+                                */
+                               config-1-9@1c000 {
+                                       reg = <0x1C000 0x1000>;
+                               };
+
+                               /*
+                                * Device tree containing only registers,
+                                * interrupts, and cpus
+                                */
+                               fdt@1d000 {
+                                       reg = <0x1D000 0x2000>;
+                               };
+
+                               /* CPU/ROM credits */
+                               credits@1f000 {
+                                       reg = <0x1F000 0x1000>;
+                               };
+                       };
+
+                       dvp0: camera@50430000 {
+                               compatible = "kendryte,k210-dvp";
+                               reg = <0x50430000 0x100>;
+                               interrupts = <24>;
+                               clocks = <&sysclk K210_CLK_DVP>;
+                               resets = <&sysrst K210_RST_DVP>;
+                               status = "disabled";
+                       };
+
+                       sysctl: syscon@50440000 {
+                               compatible = "kendryte,k210-sysctl",
+                                            "syscon", "simple-mfd";
+                               reg = <0x50440000 0x100>;
+                               reg-io-width = <4>;
+
+                               sysclk: clock-controller {
+                                       #clock-cells = <1>;
+                                       compatible = "kendryte,k210-clk";
+                                       clocks = <&in0>;
+                               };
+
+                               sysrst: reset-controller {
+                                       compatible = "kendryte,k210-rst",
+                                                    "syscon-reset";
+                                       #reset-cells = <1>;
+                                       regmap = <&sysctl>;
+                                       offset = <K210_SYSCTL_PERI_RESET>;
+                                       mask = <0x27FFFFFF>;
+                                       assert-high = <1>;
+                               };
+
+                               reboot {
+                                       compatible = "syscon-reboot";
+                                       regmap = <&sysctl>;
+                                       offset = <K210_SYSCTL_SOFT_RESET>;
+                                       mask = <1>;
+                                       value = <1>;
+                               };
+                       };
+
+                       aes0: aes@50450000 {
+                               compatible = "kendryte,k210-aes";
+                               reg = <0x50450000 0x100>;
+                               clocks = <&sysclk K210_CLK_AES>;
+                               resets = <&sysrst K210_RST_AES>;
+                               status = "disabled";
+                       };
+
+                       rtc: rtc@50460000 {
+                               compatible = "kendryte,k210-rtc";
+                               reg = <0x50460000 0x100>;
+                               clocks = <&in0>;
+                               resets = <&sysrst K210_RST_RTC>;
+                               interrupts = <20>;
+                               status = "disabled";
+                       };
+               };
+
+               apb2: bus@52000000 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "kendryte,k210-apb", "simple-pm-bus";
+                       ranges;
+                       clocks = <&sysclk K210_CLK_APB2>;
+
+                       spi0: spi@52000000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "kendryte,k210-spi",
+                                            "snps,dw-apb-ssi";
+                               reg = <0x52000000 0x100>;
+                               interrupts = <1>;
+                               clocks = <&sysclk K210_CLK_SPI0>;
+                               clock-names = "ssi_clk";
+                               resets = <&sysrst K210_RST_SPI0>;
+                               spi-max-frequency = <25000000>;
+                               num-cs = <4>;
+                               reg-io-width = <4>;
+                               status = "disabled";
+                       };
+
+                       spi1: spi@53000000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "kendryte,k210-spi",
+                                            "snps,dw-apb-ssi";
+                               reg = <0x53000000 0x100>;
+                               interrupts = <2>;
+                               clocks = <&sysclk K210_CLK_SPI1>;
+                               clock-names = "ssi_clk";
+                               resets = <&sysrst K210_RST_SPI1>;
+                               spi-max-frequency = <25000000>;
+                               num-cs = <4>;
+                               reg-io-width = <4>;
+                               status = "disabled";
+                       };
+
+                       spi3: spi@54000000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "kendryte,k210-spi",
+                                            "snps,dw-apb-ssi";
+                               reg = <0x54000000 0x200>;
+                               interrupts = <4>;
+                               clocks = <&sysclk K210_CLK_SPI3>;
+                               clock-names = "ssi_clk";
+                               resets = <&sysrst K210_RST_SPI3>;
+                               /* Could possibly go up to 200 MHz */
+                               spi-max-frequency = <100000000>;
+                               num-cs = <4>;
+                               reg-io-width = <4>;
+                               status = "disabled";
+                       };
+               };
+       };
+};
index d152074..1a15089 100644 (file)
 #define SR_SIE         _AC(0x00000002, UL) /* Supervisor Interrupt Enable */
 #define SR_SPIE                _AC(0x00000020, UL) /* Previous Supervisor IE */
 #define SR_SPP         _AC(0x00000100, UL) /* Previously Supervisor */
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define SR_PUM         _AC(0x00040000, UL) /* Protect User Memory Access */
+#else
 #define SR_SUM         _AC(0x00040000, UL) /* Supervisor User Memory Access */
+#endif
 
 #define SR_FS          _AC(0x00006000, UL) /* Floating-point Status */
 #define SR_FS_OFF      _AC(0x00000000, UL)
 #define SR_XS_CLEAN    _AC(0x00010000, UL)
 #define SR_XS_DIRTY    _AC(0x00018000, UL)
 
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define SR_VM          _AC(0x1F000000, UL) /* Virtualization Management */
+#define SR_VM_MODE_BARE        _AC(0x00000000, UL) /* No translation or protection */
+#define SR_VM_MODE_BB  _AC(0x01000000, UL) /* Single base-and-bound */
+/* Separate instruction and data base-and-bound */
+#define SR_VM_MODE_BBID        _AC(0x02000000, UL)
+#ifndef CONFIG_64BIT
+#define SR_VM_MODE_32  _AC(0x08000000, UL)
+#define SR_VM_MODE     SR_VM_MODE_32
+#else
+#define SR_VM_MODE_39  _AC(0x09000000, UL)
+#define SR_VM_MODE_48  _AC(0x0A000000, UL)
+#define SR_VM_MODE     SR_VM_MODE_39
+#endif
+#endif
+
 #ifndef CONFIG_64BIT
 #define SR_SD          _AC(0x80000000, UL) /* FS/XS dirty */
 #else
@@ -36,6 +56,7 @@
 #endif
 
 /* SATP flags */
+#ifndef CONFIG_RISCV_PRIV_1_9
 #ifndef CONFIG_64BIT
 #define SATP_PPN       _AC(0x003FFFFF, UL)
 #define SATP_MODE_32   _AC(0x80000000, UL)
@@ -45,6 +66,7 @@
 #define SATP_MODE_39   _AC(0x8000000000000000, UL)
 #define SATP_MODE      SATP_MODE_39
 #endif
+#endif
 
 /* SCAUSE */
 #define SCAUSE_IRQ_FLAG                (_AC(1, UL) << (__riscv_xlen - 1))
 #define CSR_SCAUSE             0x142
 #define CSR_STVAL              0x143
 #define CSR_SIP                        0x144
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define CSR_SPTBR              0x180
+#else
 #define CSR_SATP               0x180
+#endif
 #define CSR_MSTATUS            0x300
 #define CSR_MISA               0x301
 #define CSR_MIE                        0x304
 #define CSR_MTVEC              0x305
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define CSR_MUCOUNTEREN         0x320
+#define CSR_MSCOUNTEREN         0x321
+#define CSR_MHCOUNTEREN         0x322
+#else
 #define CSR_MCOUNTEREN         0x306
+#endif
 #define CSR_MSCRATCH           0x340
 #define CSR_MEPC               0x341
 #define CSR_MCAUSE             0x342
 #define CSR_MTVAL              0x343
 #define CSR_MIP                        0x344
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define CSR_MBASE              0x380
+#define CSR_MBOUND             0x381
+#define CSR_MIBASE             0x382
+#define CSR_MIBOUND            0x383
+#define CSR_MDBASE             0x384
+#define CSR_MDBOUND            0x385
+#endif
 #define CSR_CYCLEH             0xc80
 #define CSR_TIMEH              0xc81
 #define CSR_INSTRETH           0xc82
index 6c50149..2eb1481 100644 (file)
@@ -11,6 +11,8 @@
 #define __ASM_GBL_DATA_H
 
 #include <asm/smp.h>
+#include <asm/u-boot.h>
+#include <compiler.h>
 
 /* Architecture-specific global data */
 struct arch_global_data {
index 74de92e..1b42885 100644 (file)
@@ -51,4 +51,47 @@ void handle_ipi(ulong hart);
  */
 int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait);
 
+/**
+ * riscv_init_ipi() - Initialize inter-process interrupt (IPI) driver
+ *
+ * Platform code must provide this function. This function is called once after
+ * the cpu driver is initialized. No other riscv_*_ipi() calls will be made
+ * before this function is called.
+ *
+ * @return 0 if OK, -ve on error
+ */
+int riscv_init_ipi(void);
+
+/**
+ * riscv_send_ipi() - Send inter-processor interrupt (IPI)
+ *
+ * Platform code must provide this function.
+ *
+ * @hart: Hart ID of receiving hart
+ * @return 0 if OK, -ve on error
+ */
+int riscv_send_ipi(int hart);
+
+/**
+ * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
+ *
+ * Platform code must provide this function.
+ *
+ * @hart: Hart ID of hart to be cleared
+ * @return 0 if OK, -ve on error
+ */
+int riscv_clear_ipi(int hart);
+
+/**
+ * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
+ *
+ * Platform code must provide this function.
+ *
+ * @hart: Hart ID of hart to be checked
+ * @pending: Pointer to variable with result of the check,
+ *           1 if IPI is pending, 0 otherwise
+ * @return 0 if OK, -ve on error
+ */
+int riscv_get_ipi(int hart, int *pending);
+
 #endif
index 20529ab..5cf29df 100644 (file)
 #define SEND_IPI_TO_HART(hart)  (0x80 >> (hart))
 
 DECLARE_GLOBAL_DATA_PTR;
-static int init_plic(void);
-
-#define PLIC_BASE_GET(void)                                            \
-       do {                                                            \
-               long *ret;                                              \
-                                                                       \
-               if (!gd->arch.plic) {                                   \
-                       ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
-                       if (IS_ERR(ret))                                \
-                               return PTR_ERR(ret);                    \
-                       gd->arch.plic = ret;                            \
-                       init_plic();                                    \
-               }                                                       \
-       } while (0)
 
 static int enable_ipi(int hart)
 {
@@ -93,13 +79,21 @@ static int init_plic(void)
        return -ENODEV;
 }
 
-int riscv_send_ipi(int hart)
+int riscv_init_ipi(void)
 {
-       unsigned int ipi;
+       long *ret = syscon_get_first_range(RISCV_SYSCON_PLIC);
+
+       if (IS_ERR(ret))
+               return PTR_ERR(ret);
+       gd->arch.plic = ret;
+
+       return init_plic();
+}
 
-       PLIC_BASE_GET();
+int riscv_send_ipi(int hart)
+{
+       unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
 
-       ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
        writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
                                gd->arch.boot_hart));
 
@@ -110,8 +104,6 @@ int riscv_clear_ipi(int hart)
 {
        u32 source_id;
 
-       PLIC_BASE_GET();
-
        source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
        writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
 
@@ -120,8 +112,6 @@ int riscv_clear_ipi(int hart)
 
 int riscv_get_ipi(int hart, int *pending)
 {
-       PLIC_BASE_GET();
-
        *pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
                                                     gd->arch.boot_hart));
        *pending = !!(*pending & SEND_IPI_TO_HART(hart));
index 8779c61..6008bbe 100644 (file)
@@ -7,6 +7,7 @@
 #include <command.h>
 #include <hang.h>
 
+#ifndef CONFIG_SYSRESET
 int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
        printf("resetting ...\n");
@@ -16,3 +17,4 @@ int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 
        return 0;
 }
+#endif
index abafca9..d02e2b4 100644 (file)
@@ -8,6 +8,11 @@
 #include <asm/encoding.h>
 #include <asm/sbi.h>
 
+int riscv_init_ipi(void)
+{
+       return 0;
+}
+
 int riscv_send_ipi(int hart)
 {
        ulong mask;
index 5e0d257..78fc6c8 100644 (file)
 
 DECLARE_GLOBAL_DATA_PTR;
 
-#define CLINT_BASE_GET(void)                                           \
-       do {                                                            \
-               long *ret;                                              \
-                                                                       \
-               if (!gd->arch.clint) {                                  \
-                       ret = syscon_get_first_range(RISCV_SYSCON_CLINT); \
-                       if (IS_ERR(ret))                                \
-                               return PTR_ERR(ret);                    \
-                       gd->arch.clint = ret;                           \
-               }                                                       \
-       } while (0)
-
 int riscv_get_time(u64 *time)
 {
-       CLINT_BASE_GET();
-
        *time = readq((void __iomem *)MTIME_REG(gd->arch.clint));
 
        return 0;
@@ -47,17 +33,24 @@ int riscv_get_time(u64 *time)
 
 int riscv_set_timecmp(int hart, u64 cmp)
 {
-       CLINT_BASE_GET();
-
        writeq(cmp, (void __iomem *)MTIMECMP_REG(gd->arch.clint, hart));
 
        return 0;
 }
 
-int riscv_send_ipi(int hart)
+int riscv_init_ipi(void)
 {
-       CLINT_BASE_GET();
+       long *ret = syscon_get_first_range(RISCV_SYSCON_CLINT);
+
+       if (IS_ERR(ret))
+               return PTR_ERR(ret);
+       gd->arch.clint = ret;
+
+       return 0;
+}
 
+int riscv_send_ipi(int hart)
+{
        writel(1, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
 
        return 0;
@@ -65,8 +58,6 @@ int riscv_send_ipi(int hart)
 
 int riscv_clear_ipi(int hart)
 {
-       CLINT_BASE_GET();
-
        writel(0, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
 
        return 0;
@@ -74,8 +65,6 @@ int riscv_clear_ipi(int hart)
 
 int riscv_get_ipi(int hart, int *pending)
 {
-       CLINT_BASE_GET();
-
        *pending = readl((void __iomem *)MSIP_REG(gd->arch.clint, hart));
 
        return 0;
index 17adb35..ac22136 100644 (file)
 
 DECLARE_GLOBAL_DATA_PTR;
 
-/**
- * riscv_send_ipi() - Send inter-processor interrupt (IPI)
- *
- * Platform code must provide this function.
- *
- * @hart: Hart ID of receiving hart
- * @return 0 if OK, -ve on error
- */
-extern int riscv_send_ipi(int hart);
-
-/**
- * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
- *
- * Platform code must provide this function.
- *
- * @hart: Hart ID of hart to be cleared
- * @return 0 if OK, -ve on error
- */
-extern int riscv_clear_ipi(int hart);
-
-/**
- * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
- *
- * Platform code must provide this function.
- *
- * @hart: Hart ID of hart to be checked
- * @pending: Pointer to variable with result of the check,
- *           1 if IPI is pending, 0 otherwise
- * @return 0 if OK, -ve on error
- */
-extern int riscv_get_ipi(int hart, int *pending);
-
 static int send_ipi_many(struct ipi_data *ipi, int wait)
 {
        ofnode node, cpus;
@@ -124,7 +92,7 @@ void handle_ipi(ulong hart)
         */
        ret = riscv_clear_ipi(hart);
        if (ret) {
-               pr_err("Cannot clear IPI of hart %ld\n", hart);
+               pr_err("Cannot clear IPI of hart %ld (error %d)\n", hart, ret);
                return;
        }
 
@@ -133,14 +101,11 @@ void handle_ipi(ulong hart)
 
 int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait)
 {
-       int ret = 0;
-       struct ipi_data ipi;
-
-       ipi.addr = addr;
-       ipi.arg0 = arg0;
-       ipi.arg1 = arg1;
-
-       ret = send_ipi_many(&ipi, wait);
+       struct ipi_data ipi = {
+               .addr = addr,
+               .arg0 = arg0,
+               .arg1 = arg1,
+       };
 
-       return ret;
+       return send_ipi_many(&ipi, wait);
 }
index a6e2bfd..d2a6af0 100644 (file)
        mdio: mdio-test {
                compatible = "sandbox,mdio";
        };
+
+       pm-bus-test {
+               compatible = "simple-pm-bus";
+               clocks = <&clk_sandbox 4>;
+               power-domains = <&pwrdom 1>;
+       };
+
+       resetc2: syscon-reset {
+               compatible = "syscon-reset";
+               #reset-cells = <1>;
+               regmap = <&syscon0>;
+               offset = <1>;
+               mask = <0x27FFFFFF>;
+               assert-high = <0>;
+       };
+
+       syscon-reset-test {
+               compatible = "sandbox,misc_sandbox";
+               resets = <&resetc2 15>, <&resetc2 30>, <&resetc2 60>;
+               reset-names = "valid", "no_mask", "out_of_range";
+       };
 };
 
 #include "sandbox_pmic.dtsi"
index 1573e4a..c184c4b 100644 (file)
@@ -21,6 +21,7 @@ enum sandbox_clk_id {
        SANDBOX_CLK_ID_I2C,
        SANDBOX_CLK_ID_UART1,
        SANDBOX_CLK_ID_UART2,
+       SANDBOX_CLK_ID_BUS,
 
        SANDBOX_CLK_ID_COUNT,
 };
diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig
new file mode 100644 (file)
index 0000000..0cdcd32
--- /dev/null
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+
+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 DEFAULT_DEVICE_TREE
+       default "k210-maix-bit"
+
+config NR_CPUS
+       default 2
+
+config NR_DRAM_BANKS
+       default 3
+
+config BOARD_SPECIFIC_OPTIONS
+       def_bool y
+       select GENERIC_RISCV
+       select RISCV_PRIV_1_9
+       imply SMP
+       imply DM_SERIAL
+       imply SIFIVE_SERIAL
+       imply SIFIVE_CLINT
+       imply POWER_DOMAIN
+       imply SIMPLE_PM_BUS
+       imply CLK_CCF
+       imply CLK_COMPOSITE_CCF
+       imply CLK_K210
+       imply DM_RESET
+       imply RESET_SYSCON
+       imply SYSRESET
+       imply SYSRESET_SYSCON
+endif
diff --git a/board/sipeed/maix/MAINTAINERS b/board/sipeed/maix/MAINTAINERS
new file mode 100644 (file)
index 0000000..e7bb9ec
--- /dev/null
@@ -0,0 +1,11 @@
+Sipeed Maix BOARD
+M:     Sean Anderson <seanga2@gmail.com>
+S:     Maintained
+F:     arch/riscv/dts/k210.dtsi
+F:     arch/riscv/dts/k210-maix-bit.dts
+F:     board/sipeed/maix/
+F:     configs/sipeed_maix_bitm_defconfig
+F:     doc/board/sipeed/
+F:     include/configs/sipeed-maix.h
+F:     include/dt-bindings/*/k210-sysctl.h
+F:     test/dm/k210_pll.c
diff --git a/board/sipeed/maix/Makefile b/board/sipeed/maix/Makefile
new file mode 100644 (file)
index 0000000..4acff5b
--- /dev/null
@@ -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 (file)
index 0000000..cbcb23c
--- /dev/null
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <fdt_support.h>
+#include <asm/io.h>
+
+phys_size_t get_effective_memsize(void)
+{
+       return CONFIG_SYS_SDRAM_SIZE;
+}
+
+int board_init(void)
+{
+       int ret, i;
+       const char * const banks[] = { "sram0", "sram1", "airam" };
+       ofnode memory;
+       struct clk clk;
+
+       /* Enable RAM clocks */
+       memory = ofnode_by_compatible(ofnode_null(), "kendryte,k210-sram");
+       if (ofnode_equal(memory, ofnode_null()))
+               return -ENOENT;
+
+       for (i = 0; i < ARRAY_SIZE(banks); i++) {
+               ret = clk_get_by_name_nodev(memory, banks[i], &clk);
+               if (ret)
+                       continue;
+
+               ret = clk_enable(&clk);
+               clk_free(&clk);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
index e88136e..defddac 100644 (file)
@@ -79,6 +79,11 @@ void spl_invoke_opensbi(struct spl_image_info *spl_image)
        invalidate_icache_all();
 
 #ifdef CONFIG_SPL_SMP
+       /* Initialize the IPI before we use it */
+       ret = riscv_init_ipi();
+       if (ret)
+               hang();
+
        /*
         * Start OpenSBI on all secondary harts and wait for acknowledgment.
         *
index 20a81a0..ee1e6f3 100644 (file)
@@ -96,6 +96,7 @@ CONFIG_REGMAP=y
 CONFIG_SYSCON=y
 CONFIG_DEVRES=y
 CONFIG_DEBUG_DEVRES=y
+CONFIG_SIMPLE_PM_BUS=y
 CONFIG_ADC=y
 CONFIG_ADC_SANDBOX=y
 CONFIG_AXI=y
@@ -195,6 +196,8 @@ CONFIG_REMOTEPROC_SANDBOX=y
 CONFIG_DM_RESET=y
 CONFIG_SANDBOX_RESET=y
 CONFIG_DM_RNG=y
+CONFIG_RNG_SANDBOX=y
+CONFIG_RESET_SYSCON=y
 CONFIG_DM_RTC=y
 CONFIG_RTC_RV8803=y
 CONFIG_SANDBOX_SERIAL=y
diff --git a/configs/sipeed_maix_bitm_defconfig b/configs/sipeed_maix_bitm_defconfig
new file mode 100644 (file)
index 0000000..f48f7f0
--- /dev/null
@@ -0,0 +1,8 @@
+CONFIG_RISCV=y
+CONFIG_TARGET_SIPEED_MAIX=y
+CONFIG_ARCH_RV64I=y
+CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
+# CONFIG_NET is not set
+# CONFIG_INPUT is not set
+# CONFIG_DM_ETH is not set
+# CONFIG_EFI_LOADER is not set
index 19733a6..0a15899 100644 (file)
@@ -18,6 +18,7 @@ Board-specific doc
    renesas/index
    rockchip/index
    sifive/index
+   sipeed/index
    st/index
    tbs/index
    toradex/index
diff --git a/doc/board/sipeed/index.rst b/doc/board/sipeed/index.rst
new file mode 100644 (file)
index 0000000..3518e2d
--- /dev/null
@@ -0,0 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Sipeed
+======
+
+.. toctree::
+   :maxdepth: 2
+
+   maix
diff --git a/doc/board/sipeed/maix.rst b/doc/board/sipeed/maix.rst
new file mode 100644 (file)
index 0000000..06e0008
--- /dev/null
@@ -0,0 +1,298 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+
+Maix Bit
+========
+
+Several of the Sipeed Maix series of boards cotain the Kendryte K210 processor,
+a 64-bit RISC-V CPU. 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 available from
+`Sipeed's website <http://dl.sipeed.com/MAIX/HDK/>`_.
+Documentation for the Kendryte K210 is available from
+`Kendryte's website <https://kendryte.com/downloads/>`_. However, hardware
+details are rather lacking, so most technical reference has been taken from the
+`standalone sdk <https://github.com/kendryte/kendryte-standalone-sdk>`_.
+
+Build and boot steps
+--------------------
+
+To build u-boot, run
+
+.. code-block:: none
+
+    make sipeed_maix_bitm_defconfig
+    make CROSS_COMPILE=<your cross compile prefix>
+
+To flash u-boot to a maix bit, run
+
+.. code-block:: none
+
+    kflash -tp /dev/<your tty here> -B bit_mic u-boot-dtb.bin
+
+Boot output should look like the following:
+
+.. code-block:: none
+
+    U-Boot 2020.04-rc2-00087-g2221cc09c1-dirty (Feb 28 2020 - 13:53:09 -0500)
+
+    DRAM:  8 MiB
+    In:    serial@38000000
+    Out:   serial@38000000
+    Err:   serial@38000000
+    =>
+
+Loading Images
+^^^^^^^^^^^^^^
+
+To load a kernel, transfer it over serial.
+
+.. code-block:: none
+
+    => loady 80000000 1500000
+    ## Switch baudrate to 1500000 bps and press ENTER ...
+
+    *** baud: 1500000
+
+    *** baud: 1500000 ***
+    ## Ready for binary (ymodem) download to 0x80000000 at 1500000 bps...
+    C
+    *** file: loader.bin
+    $ sz -vv loader.bin
+    Sending: loader.bin
+    Bytes Sent:2478208   BPS:72937
+    Sending:
+    Ymodem sectors/kbytes sent:   0/ 0k
+    Transfer complete
+
+    *** exit status: 0 ***
+    ## Total Size      = 0x0025d052 = 2478162 Bytes
+    ## Switch baudrate to 115200 bps and press ESC ...
+
+    *** baud: 115200
+
+    *** baud: 115200 ***
+    =>
+
+Running Programs
+^^^^^^^^^^^^^^^^
+
+Binaries
+""""""""
+
+To run a bare binary, use the ``go`` command:
+
+.. code-block:: none
+
+    => loady
+    ## Ready for binary (ymodem) download to 0x80000000 at 115200 bps...
+    C
+    *** file: ./examples/standalone/hello_world.bin
+    $ sz -vv ./examples/standalone/hello_world.bin
+    Sending: hello_world.bin
+    Bytes Sent:   4864   BPS:649
+    Sending:
+    Ymodem sectors/kbytes sent:   0/ 0k
+    Transfer complete
+
+    *** exit status: 0 ***
+    (CAN) packets, 5 retries
+    ## Total Size      = 0x000012f8 = 4856 Bytes
+    => go 80000000
+    ## Starting application at 0x80000000 ...
+    Example expects ABI version 9
+    Actual U-Boot ABI version 9
+    Hello World
+    argc = 1
+    argv[0] = "80000000"
+    argv[1] = "<NULL>"
+    Hit any key to exit ...
+
+Legacy Images
+"""""""""""""
+
+To run legacy images, use the ``bootm`` command:
+
+.. code-block:: none
+
+    $ tools/mkimage -A riscv -O u-boot -T standalone -C none -a 80000000 -e 80000000 -d examples/standalone/hello_world.bin hello_world.img
+    Image Name:
+    Created:      Thu Mar  5 12:04:10 2020
+    Image Type:   RISC-V U-Boot Standalone Program (uncompressed)
+    Data Size:    4856 Bytes = 4.74 KiB = 0.00 MiB
+    Load Address: 80000000
+    Entry Point:  80000000
+
+    $ picocom -b 115200 /dev/ttyUSB0i
+    => loady
+    ## Ready for binary (ymodem) download to 0x80000000 at 115200 bps...
+    C
+    *** file: hello_world.img
+    $ sz -vv hello_world.img
+    Sending: hello_world.img
+    Bytes Sent:   4992   BPS:665
+    Sending:
+    Ymodem sectors/kbytes sent:   0/ 0k
+    Transfer complete
+
+    *** exit status: 0 ***
+    CAN) packets, 3 retries
+    ## Total Size      = 0x00001338 = 4920 Bytes
+    => bootm
+    ## Booting kernel from Legacy Image at 80000000 ...
+       Image Name:
+       Image Type:   RISC-V U-Boot Standalone Program (uncompressed)
+       Data Size:    4856 Bytes = 4.7 KiB
+       Load Address: 80000000
+       Entry Point:  80000000
+       Verifying Checksum ... OK
+       Loading Standalone Program
+    Example expects ABI version 9
+    Actual U-Boot ABI version 9
+    Hello World
+    argc = 0
+    argv[0] = "<NULL>"
+    Hit any key to exit ...
+
+Over- and Under-clocking
+------------------------
+
+To change the clock speed of the K210, you will need to enable
+``CONFIG_CLK_K210_SET_RATE`` and edit the board's device tree. To do this, add a
+section to ``arch/riscv/arch/riscv/dts/k210-maix-bit.dts`` like the following:
+
+.. code-block:: none
+
+    &sysclk {
+       assigned-clocks = <&sysclk K210_CLK_PLL0>;
+       assigned-clock-rates = <800000000>;
+    };
+
+There are three PLLs on the K210: PLL0 is the parent of most of the components,
+including the CPU and RAM. PLL1 is the parent of the neural network coprocessor.
+PLL2 is the parent of the sound processing devices. Note that child clocks of
+PLL0 and PLL2 run at *half* the speed of the PLLs. For example, if PLL0 is
+running at 800 MHz, then the CPU will run at 400 MHz. This is the example given
+above. The CPU can be overclocked to around 600 MHz, and underclocked to 26 MHz.
+
+It is possible to set PLL2's parent to PLL0. The plls are more accurate when
+converting between similar frequencies. This makes it easier to get an accurate
+frequency for I2S. As an example, consider sampling an I2S device at 44.1 kHz.
+On this device, the I2S serial clock runs at 64 times the sample rate.
+Therefore, we would like to run PLL2 at an even multiple of 2.8224 MHz. If
+PLL2's parent is IN0, we could use a frequency of 390 MHz (the same as the CPU's
+default speed).  Dividing by 138 yields a serial clock of about 2.8261 MHz. This
+results in a sample rate of 44.158 kHz---around 50 Hz or .1% too fast. If,
+instead, we set PLL2's parent to PLL1 running at 390 MHz, and request a rate of
+2.8224 * 136 = 383.8464 MHz, the achieved rate is 383.90625 MHz. Dividing by 136
+yields a serial clock of about 2.8228 MHz. This results in a sample rate of
+44.107 kHz---just 7 Hz or .02% too fast. This configuration is shown in the
+following example:
+
+.. code-block:: none
+
+    &sysclk {
+       assigned-clocks = <&sysclk K210_CLK_PLL1>, <&sysclk K210_CLK_PLL2>;
+       assigned-clock-parents = <0>, <&sysclk K210_CLK_PLL1>;
+       assigned-clock-rates = <390000000>, <383846400>;
+    };
+
+There are a couple of quirks to the PLLs. First, there are more frequency ratios
+just above and below 1.0, but there is a small gap around 1.0. To be explicit,
+if the input frequency is 100 MHz, it would be impossible to have an output of
+99 or 101 MHz. In addition, there is a maximum frequency for the internal VCO,
+so higher input/output frequencies will be less accurate than lower ones.
+
+Technical Details
+-----------------
+
+Boot Sequence
+^^^^^^^^^^^^^
+
+1. ``RESET`` pin is deasserted.
+2. Both harts begin executing at ``0x00001000``.
+3. Both harts jump to firmware at ``0x88000000``.
+4. One hart is chosen as a boot hart.
+5. Firmware reads value of pin ``IO_16`` (ISP).
+
+   * If the pin is low, enter ISP mode. This mode allows loading data to ram,
+     writing it to flash, and booting from specific addresses.
+   * If the pin is high, continue boot.
+6. Firmware reads the next stage from flash (SPI3) to address ``0x80000000``.
+
+   * If byte 0 is 1, the next stage is decrypted using the built-in AES
+     accelerator and the one-time programmable, 128-bit AES key.
+   * Bytes 1 to 4 hold the length of the next stage.
+   * The SHA-256 sum of the next stage is automatically calculated, and verified
+     against the 32 bytes following the next stage.
+7. The boot hart sends an IPI to the other hart telling it to jump to the next
+   stage.
+8. The boot hart jumps to ``0x80000000``.
+
+Memory Map
+^^^^^^^^^^
+
+========== ========= ===========
+Address    Size      Description
+========== ========= ===========
+0x00000000 0x1000    debug
+0x00001000 0x1000    rom
+0x02000000 0xC000    clint
+0x0C000000 0x4000000 plic
+0x38000000 0x1000    uarths
+0x38001000 0x1000    gpiohs
+0x40000000 0x400000  sram0 (non-cached)
+0x40400000 0x200000  sram1 (non-cached)
+0x40600000 0x200000  airam (non-cached)
+0x40800000 0xC00000  kpu
+0x42000000 0x400000  fft
+0x50000000 0x1000    dmac
+0x50200000 0x200000  apb0
+0x50200000 0x80      gpio
+0x50210000 0x100     uart0
+0x50220000 0x100     uart1
+0x50230000 0x100     uart2
+0x50240000 0x100     spi slave
+0x50250000 0x200     i2s0
+0x50250200 0x200     apu
+0x50260000 0x200     i2s1
+0x50270000 0x200     i2s2
+0x50280000 0x100     i2c0
+0x50290000 0x100     i2c1
+0x502A0000 0x100     i2c2
+0x502B0000 0x100     fpioa
+0x502C0000 0x100     sha256
+0x502D0000 0x100     timer0
+0x502E0000 0x100     timer1
+0x502F0000 0x100     timer2
+0x50400000 0x200000  apb1
+0x50400000 0x100     wdt0
+0x50410000 0x100     wdt1
+0x50420000 0x100     otp control
+0x50430000 0x100     dvp
+0x50440000 0x100     sysctl
+0x50450000 0x100     aes
+0x50460000 0x100     rtc
+0x52000000 0x4000000 apb2
+0x52000000 0x100     spi0
+0x53000000 0x100     spi1
+0x54000000 0x200     spi3
+0x80000000 0x400000  sram0 (cached)
+0x80400000 0x200000  sram1 (cached)
+0x80600000 0x200000  airam (cached)
+0x88000000 0x20000   otp
+0x88000000 0xC200    firmware
+0x8801C000 0x1000    riscv priv spec 1.9 config
+0x8801D000 0x2000    flattened device tree (contains only addresses and
+                     interrupts)
+0x8801f000 0x1000    credits
+========== ========= ===========
diff --git a/doc/device-tree-bindings/bus/simple-pm-bus.txt b/doc/device-tree-bindings/bus/simple-pm-bus.txt
new file mode 100644 (file)
index 0000000..6f15037
--- /dev/null
@@ -0,0 +1,44 @@
+Simple Power-Managed Bus
+========================
+
+A Simple Power-Managed Bus is a transparent bus that doesn't need a real
+driver, as it's typically initialized by the boot loader.
+
+However, its bus controller is part of a PM domain, or under the control of a
+functional clock.  Hence, the bus controller's PM domain and/or clock must be
+enabled for child devices connected to the bus (either on-SoC or externally)
+to function.
+
+While "simple-pm-bus" follows the "simple-bus" set of properties, as specified
+in the Devicetree Specification, it is not an extension of "simple-bus".
+
+
+Required properties:
+  - compatible: Must contain at least "simple-pm-bus".
+               Must not contain "simple-bus".
+               It's recommended to let this be preceded by one or more
+               vendor-specific compatible values.
+  - #address-cells, #size-cells, ranges: Must describe the mapping between
+               parent address and child address spaces.
+
+Optional platform-specific properties for clock or PM domain control (at least
+one of them is required):
+  - clocks: Must contain a reference to the functional clock(s),
+  - power-domains: Must contain a reference to the PM domain.
+Please refer to the binding documentation for the clock and/or PM domain
+providers for more details.
+
+
+Example:
+
+       bsc: bus@fec10000 {
+               compatible = "renesas,bsc-sh73a0", "renesas,bsc",
+                            "simple-pm-bus";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges = <0 0 0x20000000>;
+               reg = <0xfec10000 0x400>;
+               interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&zb_clk>;
+               power-domains = <&pd_a4s>;
+       };
diff --git a/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt b/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
new file mode 100644 (file)
index 0000000..5b24abc
--- /dev/null
@@ -0,0 +1,33 @@
+Kendryte K210 Sysctl
+
+This binding describes the K210 sysctl device, which contains many miscellaneous
+registers controlling system functionality. This node is a register map and can
+be reference by other bindings which need a phandle to the K210 sysctl regmap.
+
+Required properties:
+- compatible: should be
+       "kendryte,k210-sysctl", "syscon", "simple-mfd"
+- reg: address and length of the sysctl registers
+- reg-io-width: must be <4>
+
+Clock sub-node
+
+This node is a binding for the clock tree driver
+
+Required properties:
+- compatible: should be "kendryte,k210-clk"
+- clocks: phandle to the "in0" external oscillator
+- #clock-cells: must be <1>
+
+Example:
+sysctl: syscon@50440000 {
+       compatible = "kendryte,k210-sysctl", "syscon", "simple-mfd";
+       reg = <0x50440000 0x100>;
+       reg-io-width = <4>;
+
+       sysclk: clock-controller {
+               compatible = "kendryte,k210-clk";
+               clocks = <&in0>;
+               #clock-cells = <1>;
+       };
+};
diff --git a/doc/device-tree-bindings/reset/syscon-reset.txt b/doc/device-tree-bindings/reset/syscon-reset.txt
new file mode 100644 (file)
index 0000000..f136b3d
--- /dev/null
@@ -0,0 +1,36 @@
+Generic SYSCON mapped register reset driver
+
+This is a generic reset driver using syscon to map the reset register.
+The reset is generally performed with a write to the reset register
+defined by the register map pointed by syscon reference plus the offset and
+shifted by the reset specifier/
+
+To assert a reset on some device, the equivalent of the following operation is
+performed, where reset_id is the reset specifier from the device's resets
+property.
+
+       if (BIT(reset_id) & mask)
+               regmap[offset][reset_id] = assert-high;
+
+Required properties:
+- compatible: should contain "syscon-reset"
+- #reset-cells: must be 1
+- regmap: this is phandle to the register map node
+- offset: offset in the register map for the reboot register (in bytes)
+
+Optional properties:
+- mask: accept only the reset specifiers defined by the mask (32 bit)
+- assert-high: Bit to write when asserting a reset. Defaults to 1.
+
+Default will be little endian mode, 32 bit access only.
+
+Example:
+
+       reset-controller {
+               compatible = "syscon-reset";
+               #reset-cells = <1>;
+               regmap = <&sysctl>;
+               offset = <0x20>;
+               mask = <0x27FFFFFF>;
+               assert-high = <0>;
+       };
index 36b60dc..e40ac36 100644 (file)
@@ -1,42 +1,37 @@
 Introduction:
 =============
 
-This documentation entry describes the Common Clock Framework [CCF]
-port from Linux kernel (v5.1.12) to U-Boot.
+This documentation entry describes the Common Clock Framework [CCF] port from
+Linux kernel (v5.1.12) to U-Boot.
 
-This code is supposed to bring CCF to IMX based devices (imx6q, imx7
-imx8). Moreover, it also provides some common clock code, which would
-allow easy porting of CCF Linux code to other platforms.
+This code is supposed to bring CCF to IMX based devices (imx6q, imx7 imx8).
+Moreover, it also provides some common clock code, which would allow easy
+porting of CCF Linux code to other platforms.
 
 Design decisions:
 =================
 
-* U-Boot's driver model [DM] for clk differs from Linux CCF. The most
-  notably difference is the lack of support for hierarchical clocks and
-  "clock as a manager driver" (single clock DTS node acts as a starting
-  point for all other clocks).
+* U-Boot's driver model [DM] for clk differs from Linux CCF. The most notably
+  difference is the lack of support for hierarchical clocks and "clock as a
+  manager driver" (single clock DTS node acts as a starting point for all other
+  clocks).
 
-* The clk_get_rate() caches the previously read data if CLK_GET_RATE_NOCACHE
-  is not set (no need for recursive access).
+* The clk_get_rate() caches the previously read data if CLK_GET_RATE_NOCACHE is
+  not set (no need for recursive access).
 
-* On purpose the "manager" clk driver (clk-imx6q.c) is not using large
-  table to store pointers to clocks - e.g. clk[IMX6QDL_CLK_USDHC2_SEL] = ....
-  Instead we use udevice's linked list for the same class (UCLASS_CLK).
+* On purpose the "manager" clk driver (clk-imx6q.c) is not using large table to
+  store pointers to clocks - e.g. clk[IMX6QDL_CLK_USDHC2_SEL] = .... Instead we
+  use udevice's linked list for the same class (UCLASS_CLK).
 
   Rationale:
   ----------
-    When porting the code as is from Linux, one would need ~1KiB of RAM to
-    store it. This is way too much if we do plan to use this driver in SPL.
+    When porting the code as is from Linux, one would need ~1KiB of RAM to store
+    it. This is way too much if we do plan to use this driver in SPL.
 
 * The "central" structure of this patch series is struct udevice and its
   uclass_priv field contains the struct clk pointer (to the originally created
   one).
 
-* Up till now U-Boot's driver model (DM) CLK operates on udevice (main
-  access to clock is by udevice ops)
-  In the CCF the access to struct clk (embodying pointer to *dev) is
-  possible via dev_get_clk_ptr() (it is a wrapper on dev_get_uclass_priv()).
-
 * To keep things simple the struct udevice's uclass_priv pointer is used to
   store back pointer to corresponding struct clk. However, it is possible to
   modify clk-uclass.c file and add there struct uc_clk_priv, which would have
@@ -45,13 +40,17 @@ Design decisions:
   setting .per_device_auto_alloc_size = sizeof(struct uc_clk_priv)) the
   uclass_priv stores the pointer to struct clk.
 
+* Non-CCF clocks do not have a pointer to a clock in clk->dev->priv. In the case
+  of composite clocks, clk->dev->priv may not match clk. Drivers should always
+  use the struct clk which is passed to them, and not clk->dev->priv.
+
 * It is advised to add common clock code (like already added rate and flags) to
   the struct clk, which is a top level description of the clock.
 
 * U-Boot's driver model already provides the facility to automatically allocate
-  (via private_alloc_size) device private data (accessible via dev->priv).
-  It may look appealing to use this feature to allocate private structures for
-  CCF clk devices e.g. divider (struct clk_divider *divider) for IMX6Q clock.
+  (via private_alloc_size) device private data (accessible via dev->priv). It
+  may look appealing to use this feature to allocate private structures for CCF
+  clk devices e.g. divider (struct clk_divider *divider) for IMX6Q clock.
 
   The above feature had not been used for following reasons:
   - The original CCF Linux kernel driver is the "manager" for clocks - it
@@ -64,21 +63,23 @@ Design decisions:
 
 * I've added the clk_get_parent(), which reads parent's dev->uclass_priv to
   provide parent's struct clk pointer. This seems the easiest way to get
-  child/parent relationship for struct clk in U-Boot's udevice based clocks.
+  child/parent relationship for struct clk in U-Boot's udevice based clocks.  In
+  the future arbitrary parents may be supported by adding a get_parent function
+  to clk_ops.
 
 * Linux's CCF 'struct clk_core' corresponds to U-Boot's udevice in 'struct clk'.
   Clock IP block agnostic flags from 'struct clk_core' (e.g. NOCACHE) have been
-  moved from this struct one level up to 'struct clk'.
+  moved from this struct one level up to 'struct clk'. Many flags are
+  unimplemented at the moment.
 
 * For tests the new ./test/dm/clk_ccf.c and ./drivers/clk/clk_sandbox_ccf.c
   files have been introduced. The latter setups the CCF clock structure for
-  sandbox by reusing, if possible, generic clock primitives - like divier
-  and mux. The former file provides code to tests this setup.
+  sandbox by reusing, if possible, generic clock primitives - like divier and
+  mux. The former file provides code to tests this setup.
 
   For sandbox new CONFIG_SANDBOX_CLK_CCF Kconfig define has been introduced.
-  All new primitives added for new architectures must have corresponding test
-  in the two aforementioned files.
-
+  All new primitives added for new architectures must have corresponding test in
+  the two aforementioned files.
 
 Testing (sandbox):
 ==================
index 8b8b719..82cb187 100644 (file)
@@ -156,6 +156,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"
index e017833..d911954 100644 (file)
@@ -27,6 +27,7 @@ obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
 obj-$(CONFIG_CLK_EXYNOS) += exynos/
 obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/
 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/
index 4141850..819bfca 100644 (file)
@@ -24,7 +24,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)
@@ -34,7 +37,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)
@@ -44,7 +50,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)
@@ -54,7 +63,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 clk_get_rate(clk);
 }
 
 static int clk_composite_enable(struct clk *clk)
@@ -64,7 +76,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 0;
 }
 
 static int clk_composite_disable(struct clk *clk)
@@ -74,15 +89,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 0;
 }
 
-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,
@@ -96,7 +108,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)
@@ -105,8 +119,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;
        }
 
@@ -115,11 +127,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;
@@ -134,8 +141,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;
        }
 
@@ -147,6 +152,13 @@ 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:
@@ -154,6 +166,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,
index 2a68719..3465853 100644 (file)
@@ -73,8 +73,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;
 
@@ -153,8 +152,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;
index 2ceb6bb..0eb24b8 100644 (file)
@@ -20,8 +20,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;
 
index 23c1f2c..98e4b80 100644 (file)
@@ -46,8 +46,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;
 
@@ -89,8 +88,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)
index c69cce0..26991a5 100644 (file)
@@ -38,8 +38,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) {
@@ -82,8 +81,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)
@@ -100,8 +98,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;
 
@@ -118,8 +115,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;
index 9ffc224..70df9d4 100644 (file)
@@ -123,7 +123,7 @@ static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name,
 
 
        return clk_get_by_index_tail(ret, dev_ofnode(dev), &args, "clocks",
-                                    index > 0, clk);
+                                    index, clk);
 }
 
 int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
@@ -135,7 +135,7 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
                                         index, &args);
 
        return clk_get_by_index_tail(ret, dev_ofnode(dev), &args, "clocks",
-                                    index > 0, clk);
+                                    index, clk);
 }
 
 int clk_get_by_index_nodev(ofnode node, int index, struct clk *clk)
@@ -144,10 +144,10 @@ int clk_get_by_index_nodev(ofnode node, int index, struct clk *clk)
        int ret;
 
        ret = ofnode_parse_phandle_with_args(node, "clocks", "#clock-cells", 0,
-                                            index > 0, &args);
+                                            index, &args);
 
        return clk_get_by_index_tail(ret, node, &args, "clocks",
-                                    index > 0, clk);
+                                    index, clk);
 }
 
 int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk)
index b38890d..40b2d4c 100644 (file)
@@ -39,7 +39,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);
@@ -52,7 +52,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);
index 3d7aebb..124138c 100644 (file)
@@ -282,7 +282,7 @@ static int imx8mp_clk_probe(struct udevice *dev)
        clk_dm(IMX8MP_SYS_PLL2_1000M, imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1));
 
        base = dev_read_addr_ptr(dev);
-       if (base == (void *)FDT_ADDR_T_NONE)
+       if (!base)
                return -EINVAL;
 
        clk_dm(IMX8MP_CLK_A53_SRC, imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels)));
diff --git a/drivers/clk/kendryte/Kconfig b/drivers/clk/kendryte/Kconfig
new file mode 100644 (file)
index 0000000..073fca0
--- /dev/null
@@ -0,0 +1,12 @@
+config CLK_K210
+       bool "Clock support for Kendryte K210"
+       depends on CLK && CLK_CCF && CLK_COMPOSITE_CCF
+       help
+         This enables support clock driver for Kendryte K210 platforms.
+
+config CLK_K210_SET_RATE
+       bool "Enable setting the Kendryte K210 PLL rate"
+       depends on CLK_K210
+       help
+         Add functionality to calculate new rates for K210 PLLs. Enabling this
+         feature adds around 1K to U-Boot's final size.
diff --git a/drivers/clk/kendryte/Makefile b/drivers/clk/kendryte/Makefile
new file mode 100644 (file)
index 0000000..6fb6825
--- /dev/null
@@ -0,0 +1 @@
+obj-y += bypass.o clk.o pll.o
diff --git a/drivers/clk/kendryte/bypass.c b/drivers/clk/kendryte/bypass.c
new file mode 100644 (file)
index 0000000..d1fd281
--- /dev/null
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#define LOG_CATEGORY UCLASS_CLK
+#include <kendryte/bypass.h>
+
+#include <clk-uclass.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <log.h>
+
+#define CLK_K210_BYPASS "k210_clk_bypass"
+
+/*
+ * This is a small driver to do a software bypass of a clock if hardware bypass
+ * is not working. I have tried to write this in a generic fashion, so that it
+ * could be potentially broken out of the kendryte code at some future date.
+ *
+ * Say you have the following clock configuration
+ *
+ * +---+ +---+
+ * |osc| |pll|
+ * +---+ +---+
+ *         ^
+ *        /|
+ *       / |
+ *      /  |
+ *     /   |
+ *    /    |
+ * +---+ +---+
+ * |clk| |clk|
+ * +---+ +---+
+ *
+ * But the pll does not have a bypass, so when you configure the pll, the
+ * configuration needs to change to look like
+ *
+ * +---+ +---+
+ * |osc| |pll|
+ * +---+ +---+
+ *   ^
+ *   |\
+ *   | \
+ *   |  \
+ *   |   \
+ *   |    \
+ * +---+ +---+
+ * |clk| |clk|
+ * +---+ +---+
+ *
+ * To set this up, create a bypass clock with bypassee=pll and alt=osc. When
+ * creating the child clocks, set their parent to the bypass clock. After
+ * creating all the children, call k210_bypass_setchildren().
+ */
+
+static int k210_bypass_dobypass(struct k210_bypass *bypass)
+{
+       int ret, i;
+
+       /*
+        * If we already have saved parents, then the children are already
+        * bypassed
+        */
+       if (bypass->child_count && bypass->saved_parents[0])
+               return 0;
+
+       for (i = 0; i < bypass->child_count; i++) {
+               struct clk *child = bypass->children[i];
+               struct clk *parent = clk_get_parent(child);
+
+               if (IS_ERR(parent)) {
+                       for (; i; i--)
+                               bypass->saved_parents[i] = NULL;
+                       return PTR_ERR(parent);
+               }
+               bypass->saved_parents[i] = parent;
+       }
+
+       for (i = 0; i < bypass->child_count; i++) {
+               struct clk *child = bypass->children[i];
+
+               ret = clk_set_parent(child, bypass->alt);
+               if (ret) {
+                       for (; i; i--)
+                               clk_set_parent(bypass->children[i],
+                                              bypass->saved_parents[i]);
+                       for (i = 0; i < bypass->child_count; i++)
+                               bypass->saved_parents[i] = NULL;
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int k210_bypass_unbypass(struct k210_bypass *bypass)
+{
+       int err, ret, i;
+
+       if (!bypass->child_count && !bypass->saved_parents[0]) {
+               log_warning("Cannot unbypass children; dobypass not called first\n");
+               return 0;
+       }
+
+       ret = 0;
+       for (i = 0; i < bypass->child_count; i++) {
+               err = clk_set_parent(bypass->children[i],
+                                    bypass->saved_parents[i]);
+               if (err)
+                       ret = err;
+               bypass->saved_parents[i] = NULL;
+       }
+       return ret;
+}
+
+static ulong k210_bypass_get_rate(struct clk *clk)
+{
+       struct k210_bypass *bypass = to_k210_bypass(clk);
+       const struct clk_ops *ops = bypass->bypassee_ops;
+
+       if (ops->get_rate)
+               return ops->get_rate(bypass->bypassee);
+       else
+               return clk_get_parent_rate(bypass->bypassee);
+}
+
+static ulong k210_bypass_set_rate(struct clk *clk, unsigned long rate)
+{
+       int ret;
+       struct k210_bypass *bypass = to_k210_bypass(clk);
+       const struct clk_ops *ops = bypass->bypassee_ops;
+
+       /* Don't bother bypassing if we aren't going to set the rate */
+       if (!ops->set_rate)
+               return k210_bypass_get_rate(clk);
+
+       ret = k210_bypass_dobypass(bypass);
+       if (ret)
+               return ret;
+
+       ret = ops->set_rate(bypass->bypassee, rate);
+       if (ret < 0)
+               return ret;
+
+       return k210_bypass_unbypass(bypass);
+}
+
+static int k210_bypass_set_parent(struct clk *clk, struct clk *parent)
+{
+       struct k210_bypass *bypass = to_k210_bypass(clk);
+       const struct clk_ops *ops = bypass->bypassee_ops;
+
+       if (ops->set_parent)
+               return ops->set_parent(bypass->bypassee, parent);
+       else
+               return -ENOTSUPP;
+}
+
+/*
+ * For these next two functions, do the bypassing even if there is no
+ * en-/-disable function, since the bypassing itself can be observed in between
+ * calls.
+ */
+static int k210_bypass_enable(struct clk *clk)
+{
+       int ret;
+       struct k210_bypass *bypass = to_k210_bypass(clk);
+       const struct clk_ops *ops = bypass->bypassee_ops;
+
+       ret = k210_bypass_dobypass(bypass);
+       if (ret)
+               return ret;
+
+       if (ops->enable)
+               ret = ops->enable(bypass->bypassee);
+       else
+               ret = 0;
+       if (ret)
+               return ret;
+
+       return k210_bypass_unbypass(bypass);
+}
+
+static int k210_bypass_disable(struct clk *clk)
+{
+       int ret;
+       struct k210_bypass *bypass = to_k210_bypass(clk);
+       const struct clk_ops *ops = bypass->bypassee_ops;
+
+       ret = k210_bypass_dobypass(bypass);
+       if (ret)
+               return ret;
+
+       if (ops->disable)
+               return ops->disable(bypass->bypassee);
+       else
+               return 0;
+}
+
+static const struct clk_ops k210_bypass_ops = {
+       .get_rate = k210_bypass_get_rate,
+       .set_rate = k210_bypass_set_rate,
+       .set_parent = k210_bypass_set_parent,
+       .enable = k210_bypass_enable,
+       .disable = k210_bypass_disable,
+};
+
+int k210_bypass_set_children(struct clk *clk, struct clk **children,
+                            size_t child_count)
+{
+       struct k210_bypass *bypass = to_k210_bypass(clk);
+
+       kfree(bypass->saved_parents);
+       if (child_count) {
+               bypass->saved_parents =
+                       kcalloc(child_count, sizeof(struct clk *), GFP_KERNEL);
+               if (!bypass->saved_parents)
+                       return -ENOMEM;
+       }
+       bypass->child_count = child_count;
+       bypass->children = children;
+
+       return 0;
+}
+
+struct clk *k210_register_bypass_struct(const char *name,
+                                       const char *parent_name,
+                                       struct k210_bypass *bypass)
+{
+       int ret;
+       struct clk *clk;
+
+       clk = &bypass->clk;
+
+       ret = clk_register(clk, CLK_K210_BYPASS, name, parent_name);
+       if (ret)
+               return ERR_PTR(ret);
+
+       bypass->bypassee->dev = clk->dev;
+       return clk;
+}
+
+struct clk *k210_register_bypass(const char *name, const char *parent_name,
+                                struct clk *bypassee,
+                                const struct clk_ops *bypassee_ops,
+                                struct clk *alt)
+{
+       struct clk *clk;
+       struct k210_bypass *bypass;
+
+       bypass = kzalloc(sizeof(*bypass), GFP_KERNEL);
+       if (!bypass)
+               return ERR_PTR(-ENOMEM);
+
+       bypass->bypassee = bypassee;
+       bypass->bypassee_ops = bypassee_ops;
+       bypass->alt = alt;
+
+       clk = k210_register_bypass_struct(name, parent_name, bypass);
+       if (IS_ERR(clk))
+               kfree(bypass);
+       return clk;
+}
+
+U_BOOT_DRIVER(k210_bypass) = {
+       .name   = CLK_K210_BYPASS,
+       .id     = UCLASS_CLK,
+       .ops    = &k210_bypass_ops,
+};
diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c
new file mode 100644 (file)
index 0000000..981b3b7
--- /dev/null
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+#include <kendryte/clk.h>
+
+#include <asm/io.h>
+#include <dt-bindings/clock/k210-sysctl.h>
+#include <dt-bindings/mfd/k210-sysctl.h>
+#include <dm.h>
+#include <log.h>
+#include <mapmem.h>
+
+#include <kendryte/bypass.h>
+#include <kendryte/pll.h>
+
+/* All methods are delegated to CCF clocks */
+
+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,
+};
+
+/* Parents for muxed clocks */
+static const char * const generic_sels[] = { "in0_half", "pll0_half" };
+/* The first clock is in0, which is filled in by k210_clk_probe */
+static const char *aclk_sels[] = { NULL, "pll0_half" };
+static const char *pll2_sels[] = { NULL, "pll0", "pll1" };
+
+/*
+ * All parameters for different sub-clocks are collected into parameter arrays.
+ * These parameters are then initialized by the clock which uses them during
+ * probe. To save space, ids are automatically generated for each sub-clock by
+ * using an enum. Instead of storing a parameter struct for each clock, even for
+ * those clocks which don't use a particular type of sub-clock, we can just
+ * store the parameters for the clocks which need them.
+ *
+ * So why do it like this? Arranging all the sub-clocks together makes it very
+ * easy to find bugs in the code.
+ */
+
+#define DIV(id, off, shift, width) DIV_FLAGS(id, off, shift, width, 0)
+#define DIV_LIST \
+       DIV_FLAGS(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, \
+                 CLK_DIVIDER_POWER_OF_TWO) \
+       DIV(K210_CLK_APB0,   K210_SYSCTL_SEL0,  3,  3) \
+       DIV(K210_CLK_APB1,   K210_SYSCTL_SEL0,  6,  3) \
+       DIV(K210_CLK_APB2,   K210_SYSCTL_SEL0,  9,  3) \
+       DIV(K210_CLK_SRAM0,  K210_SYSCTL_THR0,  0,  4) \
+       DIV(K210_CLK_SRAM1,  K210_SYSCTL_THR0,  4,  4) \
+       DIV(K210_CLK_AI,     K210_SYSCTL_THR0,  8,  4) \
+       DIV(K210_CLK_DVP,    K210_SYSCTL_THR0, 12,  4) \
+       DIV(K210_CLK_ROM,    K210_SYSCTL_THR0, 16,  4) \
+       DIV(K210_CLK_SPI0,   K210_SYSCTL_THR1,  0,  8) \
+       DIV(K210_CLK_SPI1,   K210_SYSCTL_THR1,  8,  8) \
+       DIV(K210_CLK_SPI2,   K210_SYSCTL_THR1, 16,  8) \
+       DIV(K210_CLK_SPI3,   K210_SYSCTL_THR1, 24,  8) \
+       DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2,  0,  8) \
+       DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2,  8,  8) \
+       DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16,  8) \
+       DIV(K210_CLK_I2S0,   K210_SYSCTL_THR3,  0, 16) \
+       DIV(K210_CLK_I2S1,   K210_SYSCTL_THR3, 16, 16) \
+       DIV(K210_CLK_I2S2,   K210_SYSCTL_THR4,  0, 16) \
+       DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16,  8) \
+       DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24,  8) \
+       DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4,  0,  8) \
+       DIV(K210_CLK_I2C0,   K210_SYSCTL_THR5,  8,  8) \
+       DIV(K210_CLK_I2C1,   K210_SYSCTL_THR5, 16,  8) \
+       DIV(K210_CLK_I2C2,   K210_SYSCTL_THR5, 24,  8) \
+       DIV(K210_CLK_WDT0,   K210_SYSCTL_THR6,  0,  8) \
+       DIV(K210_CLK_WDT1,   K210_SYSCTL_THR6,  8,  8)
+
+#define _DIVIFY(id) K210_CLK_DIV_##id
+#define DIVIFY(id) _DIVIFY(id)
+
+enum k210_div_ids {
+#define DIV_FLAGS(id, ...) DIVIFY(id),
+       DIV_LIST
+#undef DIV_FLAGS
+};
+
+struct k210_div_params {
+       u8 off;
+       u8 shift;
+       u8 width;
+       u8 flags;
+};
+
+static const struct k210_div_params k210_divs[] = {
+#define DIV_FLAGS(id, _off, _shift, _width, _flags) \
+       [DIVIFY(id)] = { \
+               .off = (_off), \
+               .shift = (_shift), \
+               .width = (_width), \
+               .flags = (_flags), \
+       },
+       DIV_LIST
+#undef DIV_FLAGS
+};
+
+#undef DIV
+#undef DIV_LIST
+
+#define GATE_LIST \
+       GATE(K210_CLK_CPU,    K210_SYSCTL_EN_CENT,  0) \
+       GATE(K210_CLK_SRAM0,  K210_SYSCTL_EN_CENT,  1) \
+       GATE(K210_CLK_SRAM1,  K210_SYSCTL_EN_CENT,  2) \
+       GATE(K210_CLK_APB0,   K210_SYSCTL_EN_CENT,  3) \
+       GATE(K210_CLK_APB1,   K210_SYSCTL_EN_CENT,  4) \
+       GATE(K210_CLK_APB2,   K210_SYSCTL_EN_CENT,  5) \
+       GATE(K210_CLK_ROM,    K210_SYSCTL_EN_PERI,  0) \
+       GATE(K210_CLK_DMA,    K210_SYSCTL_EN_PERI,  1) \
+       GATE(K210_CLK_AI,     K210_SYSCTL_EN_PERI,  2) \
+       GATE(K210_CLK_DVP,    K210_SYSCTL_EN_PERI,  3) \
+       GATE(K210_CLK_FFT,    K210_SYSCTL_EN_PERI,  4) \
+       GATE(K210_CLK_GPIO,   K210_SYSCTL_EN_PERI,  5) \
+       GATE(K210_CLK_SPI0,   K210_SYSCTL_EN_PERI,  6) \
+       GATE(K210_CLK_SPI1,   K210_SYSCTL_EN_PERI,  7) \
+       GATE(K210_CLK_SPI2,   K210_SYSCTL_EN_PERI,  8) \
+       GATE(K210_CLK_SPI3,   K210_SYSCTL_EN_PERI,  9) \
+       GATE(K210_CLK_I2S0,   K210_SYSCTL_EN_PERI, 10) \
+       GATE(K210_CLK_I2S1,   K210_SYSCTL_EN_PERI, 11) \
+       GATE(K210_CLK_I2S2,   K210_SYSCTL_EN_PERI, 12) \
+       GATE(K210_CLK_I2C0,   K210_SYSCTL_EN_PERI, 13) \
+       GATE(K210_CLK_I2C1,   K210_SYSCTL_EN_PERI, 14) \
+       GATE(K210_CLK_I2C2,   K210_SYSCTL_EN_PERI, 15) \
+       GATE(K210_CLK_UART1,  K210_SYSCTL_EN_PERI, 16) \
+       GATE(K210_CLK_UART2,  K210_SYSCTL_EN_PERI, 17) \
+       GATE(K210_CLK_UART3,  K210_SYSCTL_EN_PERI, 18) \
+       GATE(K210_CLK_AES,    K210_SYSCTL_EN_PERI, 19) \
+       GATE(K210_CLK_FPIOA,  K210_SYSCTL_EN_PERI, 20) \
+       GATE(K210_CLK_TIMER0, K210_SYSCTL_EN_PERI, 21) \
+       GATE(K210_CLK_TIMER1, K210_SYSCTL_EN_PERI, 22) \
+       GATE(K210_CLK_TIMER2, K210_SYSCTL_EN_PERI, 23) \
+       GATE(K210_CLK_WDT0,   K210_SYSCTL_EN_PERI, 24) \
+       GATE(K210_CLK_WDT1,   K210_SYSCTL_EN_PERI, 25) \
+       GATE(K210_CLK_SHA,    K210_SYSCTL_EN_PERI, 26) \
+       GATE(K210_CLK_OTP,    K210_SYSCTL_EN_PERI, 27) \
+       GATE(K210_CLK_RTC,    K210_SYSCTL_EN_PERI, 29)
+
+#define _GATEIFY(id) K210_CLK_GATE_##id
+#define GATEIFY(id) _GATEIFY(id)
+
+enum k210_gate_ids {
+#define GATE(id, ...) GATEIFY(id),
+       GATE_LIST
+#undef GATE
+};
+
+struct k210_gate_params {
+       u8 off;
+       u8 bit_idx;
+};
+
+static const struct k210_gate_params k210_gates[] = {
+#define GATE(id, _off, _idx) \
+       [GATEIFY(id)] = { \
+               .off = (_off), \
+               .bit_idx = (_idx), \
+       },
+       GATE_LIST
+#undef GATE
+};
+
+#undef GATE_LIST
+
+#define MUX(id, reg, shift, width) \
+       MUX_PARENTS(id, generic_sels, reg, shift, width)
+#define MUX_LIST \
+       MUX_PARENTS(K210_CLK_PLL2, pll2_sels, K210_SYSCTL_PLL2, 26, 2) \
+       MUX_PARENTS(K210_CLK_ACLK, aclk_sels, K210_SYSCTL_SEL0,  0, 1) \
+       MUX(K210_CLK_SPI3,   K210_SYSCTL_SEL0, 12, 1) \
+       MUX(K210_CLK_TIMER0, K210_SYSCTL_SEL0, 13, 1) \
+       MUX(K210_CLK_TIMER1, K210_SYSCTL_SEL0, 14, 1) \
+       MUX(K210_CLK_TIMER2, K210_SYSCTL_SEL0, 15, 1)
+
+#define _MUXIFY(id) K210_CLK_MUX_##id
+#define MUXIFY(id) _MUXIFY(id)
+
+enum k210_mux_ids {
+#define MUX_PARENTS(id, ...) MUXIFY(id),
+       MUX_LIST
+#undef MUX_PARENTS
+       K210_CLK_MUX_NONE,
+};
+
+struct k210_mux_params {
+       const char *const *parent_names;
+       u8 num_parents;
+       u8 off;
+       u8 shift;
+       u8 width;
+};
+
+static const struct k210_mux_params k210_muxes[] = {
+#define MUX_PARENTS(id, parents, _off, _shift, _width) \
+       [MUXIFY(id)] = { \
+               .parent_names = (const char * const *)(parents), \
+               .num_parents = ARRAY_SIZE(parents), \
+               .off = (_off), \
+               .shift = (_shift), \
+               .width = (_width), \
+       },
+       MUX_LIST
+#undef MUX_PARENTS
+};
+
+#undef MUX
+#undef MUX_LIST
+
+struct k210_pll_params {
+       u8 off;
+       u8 lock_off;
+       u8 shift;
+       u8 width;
+};
+
+static const struct k210_pll_params k210_plls[] = {
+#define PLL(_off, _shift, _width) { \
+       .off = (_off), \
+       .lock_off = K210_SYSCTL_PLL_LOCK, \
+       .shift = (_shift), \
+       .width = (_width), \
+}
+       [0] = PLL(K210_SYSCTL_PLL0,  0, 2),
+       [1] = PLL(K210_SYSCTL_PLL1,  8, 1),
+       [2] = PLL(K210_SYSCTL_PLL2, 16, 1),
+#undef PLL
+};
+
+#define COMP(id) \
+       COMP_FULL(id, MUXIFY(id), DIVIFY(id), GATEIFY(id))
+#define COMP_NOMUX(id) \
+       COMP_FULL(id, K210_CLK_MUX_NONE, DIVIFY(id), GATEIFY(id))
+#define COMP_LIST \
+       COMP(K210_CLK_SPI3) \
+       COMP(K210_CLK_TIMER0) \
+       COMP(K210_CLK_TIMER1) \
+       COMP(K210_CLK_TIMER2) \
+       COMP_NOMUX(K210_CLK_SRAM0) \
+       COMP_NOMUX(K210_CLK_SRAM1) \
+       COMP_NOMUX(K210_CLK_ROM) \
+       COMP_NOMUX(K210_CLK_DVP) \
+       COMP_NOMUX(K210_CLK_APB0) \
+       COMP_NOMUX(K210_CLK_APB1) \
+       COMP_NOMUX(K210_CLK_APB2) \
+       COMP_NOMUX(K210_CLK_AI) \
+       COMP_NOMUX(K210_CLK_I2S0) \
+       COMP_NOMUX(K210_CLK_I2S1) \
+       COMP_NOMUX(K210_CLK_I2S2) \
+       COMP_NOMUX(K210_CLK_WDT0) \
+       COMP_NOMUX(K210_CLK_WDT1) \
+       COMP_NOMUX(K210_CLK_SPI0) \
+       COMP_NOMUX(K210_CLK_SPI1) \
+       COMP_NOMUX(K210_CLK_SPI2) \
+       COMP_NOMUX(K210_CLK_I2C0) \
+       COMP_NOMUX(K210_CLK_I2C1) \
+       COMP_NOMUX(K210_CLK_I2C2)
+
+#define _COMPIFY(id) K210_CLK_COMP_##id
+#define COMPIFY(id) _COMPIFY(id)
+
+enum k210_comp_ids {
+#define COMP_FULL(id, ...) COMPIFY(id),
+       COMP_LIST
+#undef COMP_FULL
+};
+
+struct k210_comp_params {
+       u8 mux;
+       u8 div;
+       u8 gate;
+};
+
+static const struct k210_comp_params k210_comps[] = {
+#define COMP_FULL(id, _mux, _div, _gate) \
+       [COMPIFY(id)] = { \
+               .mux = (_mux), \
+               .div = (_div), \
+               .gate = (_gate), \
+       },
+       COMP_LIST
+#undef COMP_FULL
+};
+
+#undef COMP
+#undef COMP_ID
+#undef COMP_NOMUX
+#undef COMP_NOMUX_ID
+#undef COMP_LIST
+
+static struct clk *k210_bypass_children = {
+       NULL,
+};
+
+/* Helper functions to create sub-clocks */
+static struct clk_mux *k210_create_mux(const struct k210_mux_params *params,
+                                      void *base)
+{
+       struct clk_mux *mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+
+       if (!mux)
+               return mux;
+
+       mux->reg = base + params->off;
+       mux->mask = BIT(params->width) - 1;
+       mux->shift = params->shift;
+       mux->parent_names = params->parent_names;
+       mux->num_parents = params->num_parents;
+
+       return mux;
+}
+
+static struct clk_divider *k210_create_div(const struct k210_div_params *params,
+                                          void *base)
+{
+       struct clk_divider *div = kzalloc(sizeof(*div), GFP_KERNEL);
+
+       if (!div)
+               return div;
+
+       div->reg = base + params->off;
+       div->shift = params->shift;
+       div->width = params->width;
+       div->flags = params->flags;
+
+       return div;
+}
+
+static struct clk_gate *k210_create_gate(const struct k210_gate_params *params,
+                                        void *base)
+{
+       struct clk_gate *gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+
+       if (!gate)
+               return gate;
+
+       gate->reg = base + params->off;
+       gate->bit_idx = params->bit_idx;
+
+       return gate;
+}
+
+static struct k210_pll *k210_create_pll(const struct k210_pll_params *params,
+                                       void *base)
+{
+       struct k210_pll *pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+
+       if (!pll)
+               return pll;
+
+       pll->reg = base + params->off;
+       pll->lock = base + params->lock_off;
+       pll->shift = params->shift;
+       pll->width = params->width;
+
+       return pll;
+}
+
+/* Create all sub-clocks, and then register the composite clock */
+static struct clk *k210_register_comp(const struct k210_comp_params *params,
+                                     void *base, const char *name,
+                                     const char *parent)
+{
+       const char *const *parent_names;
+       int num_parents;
+       struct clk *comp;
+       const struct clk_ops *mux_ops;
+       struct clk_mux *mux;
+       struct clk_divider *div;
+       struct clk_gate *gate;
+
+       if (params->mux == K210_CLK_MUX_NONE) {
+               if (!parent)
+                       return ERR_PTR(-EINVAL);
+
+               mux_ops = NULL;
+               mux = NULL;
+               parent_names = &parent;
+               num_parents = 1;
+       } else {
+               mux_ops = &clk_mux_ops;
+               mux = k210_create_mux(&k210_muxes[params->mux], base);
+               if (!mux)
+                       return ERR_PTR(-ENOMEM);
+
+               parent_names = mux->parent_names;
+               num_parents = mux->num_parents;
+       }
+
+       div = k210_create_div(&k210_divs[params->div], base);
+       if (!div) {
+               comp = ERR_PTR(-ENOMEM);
+               goto cleanup_mux;
+       }
+
+       gate = k210_create_gate(&k210_gates[params->gate], base);
+       if (!gate) {
+               comp = ERR_PTR(-ENOMEM);
+               goto cleanup_div;
+       }
+
+       comp = clk_register_composite(NULL, name, parent_names, num_parents,
+                                     &mux->clk, mux_ops,
+                                     &div->clk, &clk_divider_ops,
+                                     &gate->clk, &clk_gate_ops, 0);
+       if (IS_ERR(comp))
+               goto cleanup_gate;
+       return comp;
+
+cleanup_gate:
+       free(gate);
+cleanup_div:
+       free(div);
+cleanup_mux:
+       if (mux)
+               free(mux);
+       return comp;
+}
+
+static bool probed;
+
+static int k210_clk_probe(struct udevice *dev)
+{
+       int ret;
+       const char *in0;
+       struct clk *in0_clk, *bypass;
+       struct clk_mux *mux;
+       struct clk_divider *div;
+       struct k210_pll *pll;
+       void *base;
+
+       /*
+        * Only one instance of this driver allowed. This prevents weird bugs
+        * when the driver fails part-way through probing. Some clocks will
+        * already have been registered, and re-probing will register them
+        * again, creating a bunch of duplicates. Better error-handling/cleanup
+        * could fix this, but it's Probably Not Worth It (TM).
+        */
+       if (probed)
+               return -ENOTSUPP;
+
+       base = dev_read_addr_ptr(dev_get_parent(dev));
+       if (!base)
+               return -EINVAL;
+
+       in0_clk = kzalloc(sizeof(*in0_clk), GFP_KERNEL);
+       if (!in0_clk)
+               return -ENOMEM;
+
+       ret = clk_get_by_index(dev, 0, in0_clk);
+       if (ret)
+               return ret;
+       in0 = in0_clk->dev->name;
+
+       probed = true;
+
+       aclk_sels[0] = in0;
+       pll2_sels[0] = in0;
+
+       /*
+        * All PLLs have a broken bypass, but pll0 has the CPU downstream, so we
+        * need to manually reparent it whenever we configure pll0
+        */
+       pll = k210_create_pll(&k210_plls[0], base);
+       if (pll) {
+               bypass = k210_register_bypass("pll0", in0, &pll->clk,
+                                             &k210_pll_ops, in0_clk);
+               clk_dm(K210_CLK_PLL0, bypass);
+       } else {
+               return -ENOMEM;
+       }
+
+       {
+               const struct k210_pll_params *params = &k210_plls[1];
+
+               clk_dm(K210_CLK_PLL1,
+                      k210_register_pll("pll1", in0, base + params->off,
+                                        base + params->lock_off, params->shift,
+                                        params->width));
+       }
+
+       /* PLL2 is muxed, so set up a composite clock */
+       mux = k210_create_mux(&k210_muxes[MUXIFY(K210_CLK_PLL2)], base);
+       pll = k210_create_pll(&k210_plls[2], base);
+       if (!mux || !pll) {
+               free(mux);
+               free(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 */
+       clk_dm(K210_CLK_IN0_H,  k210_clk_half("in0_half", in0));
+       clk_dm(K210_CLK_PLL0_H, k210_clk_half("pll0_half", "pll0"));
+       clk_dm(K210_CLK_PLL2_H, k210_clk_half("pll2_half", "pll2"));
+
+       /* ACLK has no gate */
+       mux = k210_create_mux(&k210_muxes[MUXIFY(K210_CLK_ACLK)], base);
+       div = k210_create_div(&k210_divs[DIVIFY(K210_CLK_ACLK)], base);
+       if (!mux || !div) {
+               free(mux);
+               free(div);
+       } else {
+               struct 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);
+               clk_dm(K210_CLK_ACLK, aclk);
+               if (!IS_ERR(aclk)) {
+                       k210_bypass_children = aclk;
+                       k210_bypass_set_children(bypass,
+                                                &k210_bypass_children, 1);
+               }
+       }
+
+#define REGISTER_COMP(id, name) \
+       clk_dm(id, \
+              k210_register_comp(&k210_comps[COMPIFY(id)], base, name, NULL))
+       REGISTER_COMP(K210_CLK_SPI3,   "spi3");
+       REGISTER_COMP(K210_CLK_TIMER0, "timer0");
+       REGISTER_COMP(K210_CLK_TIMER1, "timer1");
+       REGISTER_COMP(K210_CLK_TIMER2, "timer2");
+#undef REGISTER_COMP
+
+       /* Dividing clocks, no mux */
+#define REGISTER_COMP_NOMUX(id, name, parent) \
+       clk_dm(id, \
+              k210_register_comp(&k210_comps[COMPIFY(id)], base, name, parent))
+       REGISTER_COMP_NOMUX(K210_CLK_SRAM0, "sram0", "aclk");
+       REGISTER_COMP_NOMUX(K210_CLK_SRAM1, "sram1", "aclk");
+       REGISTER_COMP_NOMUX(K210_CLK_ROM,   "rom",   "aclk");
+       REGISTER_COMP_NOMUX(K210_CLK_DVP,   "dvp",   "aclk");
+       REGISTER_COMP_NOMUX(K210_CLK_APB0,  "apb0",  "aclk");
+       REGISTER_COMP_NOMUX(K210_CLK_APB1,  "apb1",  "aclk");
+       REGISTER_COMP_NOMUX(K210_CLK_APB2,  "apb2",  "aclk");
+       REGISTER_COMP_NOMUX(K210_CLK_AI,    "ai",    "pll1");
+       REGISTER_COMP_NOMUX(K210_CLK_I2S0,  "i2s0",  "pll2_half");
+       REGISTER_COMP_NOMUX(K210_CLK_I2S1,  "i2s1",  "pll2_half");
+       REGISTER_COMP_NOMUX(K210_CLK_I2S2,  "i2s2",  "pll2_half");
+       REGISTER_COMP_NOMUX(K210_CLK_WDT0,  "wdt0",  "in0_half");
+       REGISTER_COMP_NOMUX(K210_CLK_WDT1,  "wdt1",  "in0_half");
+       REGISTER_COMP_NOMUX(K210_CLK_SPI0,  "spi0",  "pll0_half");
+       REGISTER_COMP_NOMUX(K210_CLK_SPI1,  "spi1",  "pll0_half");
+       REGISTER_COMP_NOMUX(K210_CLK_SPI2,  "spi2",  "pll0_half");
+       REGISTER_COMP_NOMUX(K210_CLK_I2C0,  "i2c0",  "pll0_half");
+       REGISTER_COMP_NOMUX(K210_CLK_I2C1,  "i2c1",  "pll0_half");
+       REGISTER_COMP_NOMUX(K210_CLK_I2C2,  "i2c2",  "pll0_half");
+#undef REGISTER_COMP_NOMUX
+
+       /* Dividing clocks */
+#define REGISTER_DIV(id, name, parent) do {\
+       const struct k210_div_params *params = &k210_divs[DIVIFY(id)]; \
+       clk_dm(id, \
+              clk_register_divider(NULL, name, parent, 0, base + params->off, \
+                                   params->shift, params->width, 0)); \
+} while (false)
+       REGISTER_DIV(K210_CLK_I2S0_M, "i2s0_m", "pll2_half");
+       REGISTER_DIV(K210_CLK_I2S1_M, "i2s1_m", "pll2_half");
+       REGISTER_DIV(K210_CLK_I2S2_M, "i2s2_m", "pll2_half");
+#undef REGISTER_DIV
+
+       /* Gated clocks */
+#define REGISTER_GATE(id, name, parent) do { \
+       const struct k210_gate_params *params = &k210_gates[GATEIFY(id)]; \
+       clk_dm(id, \
+              clk_register_gate(NULL, name, parent, 0, base + params->off, \
+                                params->bit_idx, 0, NULL)); \
+} while (false)
+       REGISTER_GATE(K210_CLK_CPU,   "cpu",   "aclk");
+       REGISTER_GATE(K210_CLK_DMA,   "dma",   "aclk");
+       REGISTER_GATE(K210_CLK_FFT,   "fft",   "aclk");
+       REGISTER_GATE(K210_CLK_GPIO,  "gpio",  "apb0");
+       REGISTER_GATE(K210_CLK_UART1, "uart1", "apb0");
+       REGISTER_GATE(K210_CLK_UART2, "uart2", "apb0");
+       REGISTER_GATE(K210_CLK_UART3, "uart3", "apb0");
+       REGISTER_GATE(K210_CLK_FPIOA, "fpioa", "apb0");
+       REGISTER_GATE(K210_CLK_SHA,   "sha",   "apb0");
+       REGISTER_GATE(K210_CLK_AES,   "aes",   "apb1");
+       REGISTER_GATE(K210_CLK_OTP,   "otp",   "apb1");
+       REGISTER_GATE(K210_CLK_RTC,   "rtc",   in0);
+#undef REGISTER_GATE
+
+       return 0;
+}
+
+static const struct udevice_id k210_clk_ids[] = {
+       { .compatible = "kendryte,k210-clk" },
+       { },
+};
+
+U_BOOT_DRIVER(k210_clk) = {
+       .name = "k210_clk",
+       .id = UCLASS_CLK,
+       .of_match = k210_clk_ids,
+       .ops = &k210_clk_ops,
+       .probe = k210_clk_probe,
+};
diff --git a/drivers/clk/kendryte/pll.c b/drivers/clk/kendryte/pll.c
new file mode 100644 (file)
index 0000000..19e3588
--- /dev/null
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+#define LOG_CATEGORY UCLASS_CLK
+#include <kendryte/pll.h>
+
+#include <asm/io.h>
+/* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */
+#include <div64.h>
+#include <dt-bindings/clock/k210-sysctl.h>
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <log.h>
+#include <serial.h>
+
+#define CLK_K210_PLL "k210_clk_pll"
+
+#ifdef CONFIG_CLK_K210_SET_RATE
+static int k210_pll_enable(struct clk *clk);
+static int k210_pll_disable(struct clk *clk);
+
+/*
+ * The PLL included with the Kendryte K210 appears to be a True Circuits, Inc.
+ * General-Purpose PLL. The logical layout of the PLL with internal feedback is
+ * approximately the following:
+ *
+ *  +---------------+
+ *  |reference clock|
+ *  +---------------+
+ *          |
+ *          v
+ *        +--+
+ *        |/r|
+ *        +--+
+ *          |
+ *          v
+ *   +-------------+
+ *   |divided clock|
+ *   +-------------+
+ *          |
+ *          v
+ *  +--------------+
+ *  |phase detector|<---+
+ *  +--------------+    |
+ *          |           |
+ *          v   +--------------+
+ *        +---+ |feedback clock|
+ *        |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 divided
+ * clock's minimum and maximum frequencies have a ratio of around 128.  This
+ * leaves fairly substantial room to 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),
+};
+
+TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in,
+                                    struct k210_pll_config *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 21.25 MHz due to limitations on the
+        * VCO frequency. These are not the same limits as below because od can
+        * reduce the output frequency by 16.
+        */
+       if (rate > 1750000000 || rate < 21250000)
+               return -EINVAL;
+
+       /* Similar restrictions on the input rate */
+       if (rate_in > 1750000000 || rate_in < 13300000)
+               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 divided clock (rate_in / r) must stay between 1.75 GHz and 13.3
+        * 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, 13300000);
+
+       /* Variables get immediately incremented, so start at -1th iteration */
+       i = -1;
+       f = 0;
+       r = 0;
+       od = 0;
+       best_error = S64_MAX;
+       error = best_error;
+       /* 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 < 340000000)
+                                       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_config config = {};
+       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_config(rate, rate_in, &config);
+       if (err)
+               return err;
+       log_debug("Got r=%u f=%u od=%u\n", config.r, config.f, config.od);
+
+       /*
+        * Don't use clk_disable as it might not actually disable the pll due to
+        * refcounting
+        */
+       k210_pll_disable(clk);
+
+       reg = readl(pll->reg);
+       reg &= ~K210_PLL_CLKR
+           &  ~K210_PLL_CLKF
+           &  ~K210_PLL_CLKOD
+           &  ~K210_PLL_BWADJ;
+       reg |= FIELD_PREP(K210_PLL_CLKR, config.r - 1)
+           |  FIELD_PREP(K210_PLL_CLKF, config.f - 1)
+           |  FIELD_PREP(K210_PLL_CLKOD, config.od - 1)
+           |  FIELD_PREP(K210_PLL_BWADJ, config.f - 1);
+       writel(reg, pll->reg);
+
+       err = k210_pll_enable(clk);
+       if (err)
+               return err;
+
+       serial_setbrg();
+       return clk_get_rate(clk);
+}
+#endif /* CONFIG_CLK_K210_SET_RATE */
+
+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 || (reg & K210_PLL_BYPASS))
+               return rate_in;
+
+       if (!(reg & K210_PLL_PWRD))
+               return 0;
+
+       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);
+}
+
+/*
+ * 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)
+{
+       u32 mask = GENMASK(pll->width - 1, 0) << pll->shift;
+
+       while (true) {
+               u32 reg = readl(pll->lock);
+
+               if ((reg & mask) == mask)
+                       break;
+
+               reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP);
+               writel(reg, pll->lock);
+       }
+}
+
+/* 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);
+
+       if ((reg | K210_PLL_PWRD) && !(reg | K210_PLL_RESET))
+               return 0;
+
+       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);
+       nop();
+       nop();
+       reg &= ~K210_PLL_RESET;
+       writel(reg, pll->reg);
+
+       k210_pll_waitfor_lock(pll);
+
+       reg &= ~K210_PLL_BYPASS;
+       writel(reg, pll->reg);
+
+       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,
+#ifdef CONFIG_CLK_K210_SET_RATE
+       .set_rate = k210_pll_set_rate,
+#endif
+       .enable = k210_pll_enable,
+       .disable = k210_pll_disable,
+};
+
+struct clk *k210_register_pll_struct(const char *name, const char *parent_name,
+                                    struct k210_pll *pll)
+{
+       int ret;
+       struct clk *clk = &pll->clk;
+
+       ret = clk_register(clk, CLK_K210_PLL, name, parent_name);
+       if (ret)
+               return ERR_PTR(ret);
+       return clk;
+}
+
+struct clk *k210_register_pll(const char *name, const char *parent_name,
+                             void __iomem *reg, void __iomem *lock, u8 shift,
+                             u8 width)
+{
+       struct clk *clk;
+       struct k210_pll *pll;
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (!pll)
+               return ERR_PTR(-ENOMEM);
+       pll->reg = reg;
+       pll->lock = lock;
+       pll->shift = shift;
+       pll->width = width;
+
+       clk = k210_register_pll_struct(name, parent_name, pll);
+       if (IS_ERR(clk))
+               kfree(pll);
+       return clk;
+}
+
+U_BOOT_DRIVER(k210_pll) = {
+       .name   = CLK_K210_PLL,
+       .id     = UCLASS_CLK,
+       .ops    = &k210_pll_ops,
+};
index a3b0399..a594899 100644 (file)
@@ -195,6 +195,13 @@ config SPL_SIMPLE_BUS
          Supports the 'simple-bus' driver, which is used on some systems
          in SPL.
 
+config SIMPLE_PM_BUS
+       bool "Support simple-pm-bus driver"
+       depends on DM && OF_CONTROL && CLK && POWER_DOMAIN
+       help
+         Supports the 'simple-pm-bus' driver, which is used for busses that
+         have power domains and/or clocks which need to be enabled before use.
+
 config OF_TRANSLATE
        bool "Translate addresses using fdt_translate_address"
        depends on DM && OF_CONTROL
index c707026..10f4bec 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_$(SPL_TPL_)ACPIGEN) += acpi.o
 obj-$(CONFIG_DEVRES) += devres.o
 obj-$(CONFIG_$(SPL_)DM_DEVICE_REMOVE)  += device-remove.o
 obj-$(CONFIG_$(SPL_)SIMPLE_BUS)        += simple-bus.o
+obj-$(CONFIG_SIMPLE_PM_BUS)    += simple-pm-bus.o
 obj-$(CONFIG_DM)       += dump.o
 obj-$(CONFIG_$(SPL_TPL_)REGMAP)        += regmap.o
 obj-$(CONFIG_$(SPL_TPL_)SYSCON)        += syscon-uclass.o
index 85b3b7a..8bb456b 100644 (file)
@@ -167,7 +167,7 @@ void *dev_read_addr_ptr(const struct udevice *dev)
 {
        fdt_addr_t addr = dev_read_addr(dev);
 
-       return (addr == FDT_ADDR_T_NONE) ? NULL : map_sysmem(addr, 0);
+       return (addr == FDT_ADDR_T_NONE) ? NULL : (void *)(uintptr_t)addr;
 }
 
 void *dev_remap_addr(const struct udevice *dev)
diff --git a/drivers/core/simple-pm-bus.c b/drivers/core/simple-pm-bus.c
new file mode 100644 (file)
index 0000000..51dc9b2
--- /dev/null
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+
+/*
+ * Power domains are taken care of by driver_probe, so we just have to enable
+ * clocks
+ */
+static int simple_pm_bus_probe(struct udevice *dev)
+{
+       int ret;
+       struct clk_bulk *bulk = dev_get_priv(dev);
+
+       ret = clk_get_bulk(dev, bulk);
+       if (ret)
+               return ret;
+
+       ret = clk_enable_bulk(bulk);
+       if (ret && ret != -ENOSYS && ret != -ENOTSUPP) {
+               clk_release_bulk(bulk);
+               return ret;
+       }
+       return 0;
+}
+
+static int simple_pm_bus_remove(struct udevice *dev)
+{
+       int ret;
+       struct clk_bulk *bulk = dev_get_priv(dev);
+
+       ret = clk_release_bulk(bulk);
+       if (ret && ret != -ENOSYS && ret != -ENOTSUPP)
+               return ret;
+       else
+               return 0;
+}
+
+static const struct udevice_id simple_pm_bus_ids[] = {
+       { .compatible = "simple-pm-bus" },
+       { }
+};
+
+U_BOOT_DRIVER(simple_pm_bus_drv) = {
+       .name   = "simple_pm_bus",
+       .id     = UCLASS_SIMPLE_BUS,
+       .of_match = simple_pm_bus_ids,
+       .probe = simple_pm_bus_probe,
+       .remove = simple_pm_bus_remove,
+       .priv_auto_alloc_size = sizeof(struct clk_bulk),
+       .flags  = DM_FLAG_PRE_RELOC,
+};
index 7418c26..cbb4419 100644 (file)
@@ -86,6 +86,9 @@ int cpu_get_info(struct udevice *dev, struct cpu_info *info)
        if (!ops->get_info)
                return -ENOSYS;
 
+       /* Init cpu_info to 0 */
+       memset(info, 0, sizeof(struct cpu_info));
+
        return ops->get_info(dev, info);
 }
 
index cb04f56..100fe55 100644 (file)
@@ -1,8 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
  */
 
+#include <clk.h>
 #include <common.h>
 #include <cpu.h>
 #include <dm.h>
@@ -11,6 +13,7 @@
 #include <dm/device-internal.h>
 #include <dm/lists.h>
 #include <linux/bitops.h>
+#include <linux/err.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -29,14 +32,38 @@ static int riscv_cpu_get_desc(struct udevice *dev, char *buf, int size)
 
 static int riscv_cpu_get_info(struct udevice *dev, struct cpu_info *info)
 {
+       int ret;
+       struct clk clk;
        const char *mmu;
+       u32 i_cache_size;
+       u32 d_cache_size;
+
+       /* First try getting the frequency from the assigned clock */
+       ret = clk_get_by_index(dev, 0, &clk);
+       if (!ret) {
+               ret = clk_get_rate(&clk);
+               if (!IS_ERR_VALUE(ret))
+                       info->cpu_freq = ret;
+               clk_free(&clk);
+       }
 
-       dev_read_u32(dev, "clock-frequency", (u32 *)&info->cpu_freq);
+       if (!info->cpu_freq)
+               dev_read_u32(dev, "clock-frequency", (u32 *)&info->cpu_freq);
 
        mmu = dev_read_string(dev, "mmu-type");
-       if (!mmu)
+       if (mmu)
                info->features |= BIT(CPU_FEAT_MMU);
 
+       /* check if I cache is present */
+       ret = dev_read_u32(dev, "i-cache-size", &i_cache_size);
+       if (ret)
+               /* if not found check if d-cache is present */
+               ret = dev_read_u32(dev, "d-cache-size", &d_cache_size);
+
+       /* if either I or D cache is present set L1 cache feature */
+       if (!ret)
+               info->features |= BIT(CPU_FEAT_L1_CACHE);
+
        return 0;
 }
 
@@ -102,6 +129,24 @@ static int riscv_cpu_bind(struct udevice *dev)
        return 0;
 }
 
+static int riscv_cpu_probe(struct udevice *dev)
+{
+       int ret = 0;
+       struct clk clk;
+
+       /* Get a clock if it exists */
+       ret = clk_get_by_index(dev, 0, &clk);
+       if (ret)
+               return 0;
+
+       ret = clk_enable(&clk);
+       clk_free(&clk);
+       if (ret == -ENOSYS || ret == -ENOTSUPP)
+               return 0;
+       else
+               return ret;
+}
+
 static const struct cpu_ops riscv_cpu_ops = {
        .get_desc       = riscv_cpu_get_desc,
        .get_info       = riscv_cpu_get_info,
@@ -118,6 +163,7 @@ U_BOOT_DRIVER(riscv_cpu) = {
        .id = UCLASS_CPU,
        .of_match = riscv_cpu_ids,
        .bind = riscv_cpu_bind,
+       .probe = riscv_cpu_probe,
        .ops = &riscv_cpu_ops,
        .flags = DM_FLAG_PRE_RELOC,
 };
index f44af6c..c22d534 100644 (file)
@@ -117,7 +117,7 @@ int bcm283x_pinctl_probe(struct udevice *dev)
        }
 
        priv->base_reg = dev_read_addr_ptr(dev);
-       if (priv->base_reg == (void *)FDT_ADDR_T_NONE) {
+       if (!priv->base_reg) {
                debug("%s: Failed to get base address\n", __func__);
                return -EINVAL;
        }
index 5fdc150..e8187a3 100644 (file)
@@ -631,7 +631,7 @@ int mtk_pinctrl_common_probe(struct udevice *dev,
        int ret;
 
        priv->base = dev_read_addr_ptr(dev);
-       if (priv->base == (void *)FDT_ADDR_T_NONE)
+       if (!priv->base)
                return -EINVAL;
 
        priv->soc = soc;
index 88d3be1..58ba0c6 100644 (file)
@@ -148,4 +148,9 @@ config RESET_IMX7
        help
          Support for reset controller on i.MX7/8 SoCs.
 
+config RESET_SYSCON
+       bool "Enable generic syscon reset driver support"
+       depends on DM_RESET
+       help
+         Support generic syscon mapped register reset devices.
 endmenu
index 0a044d5..433f1ec 100644 (file)
@@ -23,3 +23,4 @@ obj-$(CONFIG_RESET_MTMIPS) += reset-mtmips.o
 obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
 obj-$(CONFIG_RESET_HISILICON) += reset-hisilicon.o
 obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
+obj-$(CONFIG_RESET_SYSCON) += reset-syscon.o
diff --git a/drivers/reset/reset-syscon.c b/drivers/reset/reset-syscon.c
new file mode 100644 (file)
index 0000000..8520227
--- /dev/null
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Sean Anderson
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <regmap.h>
+#include <reset.h>
+#include <reset-uclass.h>
+#include <syscon.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+
+struct syscon_reset_priv {
+       struct regmap *regmap;
+       uint offset;
+       uint mask;
+       bool assert_high;
+};
+
+static int syscon_reset_request(struct reset_ctl *rst)
+{
+       struct syscon_reset_priv *priv = dev_get_priv(rst->dev);
+
+       if (BIT(rst->id) & priv->mask)
+               return 0;
+       else
+               return -EINVAL;
+}
+
+static int syscon_reset_assert(struct reset_ctl *rst)
+{
+       struct syscon_reset_priv *priv = dev_get_priv(rst->dev);
+
+       return regmap_update_bits(priv->regmap, priv->offset, BIT(rst->id),
+                                 priv->assert_high ? BIT(rst->id) : 0);
+}
+
+static int syscon_reset_deassert(struct reset_ctl *rst)
+{
+       struct syscon_reset_priv *priv = dev_get_priv(rst->dev);
+
+       return regmap_update_bits(priv->regmap, priv->offset, BIT(rst->id),
+                                 priv->assert_high ? 0 : BIT(rst->id));
+}
+
+static const struct reset_ops syscon_reset_ops = {
+       .request = syscon_reset_request,
+       .rst_assert = syscon_reset_assert,
+       .rst_deassert = syscon_reset_deassert,
+};
+
+int syscon_reset_probe(struct udevice *dev)
+{
+       struct syscon_reset_priv *priv = dev_get_priv(dev);
+
+       priv->regmap = syscon_regmap_lookup_by_phandle(dev, "regmap");
+       if (IS_ERR(priv->regmap))
+               return -ENODEV;
+
+       priv->offset = dev_read_u32_default(dev, "offset", 0);
+       priv->mask = dev_read_u32_default(dev, "mask", 0);
+       priv->assert_high = dev_read_u32_default(dev, "assert-high", true);
+
+       return 0;
+}
+
+static const struct udevice_id syscon_reset_ids[] = {
+       { .compatible = "syscon-reset" },
+       { },
+};
+
+U_BOOT_DRIVER(syscon_reset) = {
+       .name = "syscon_reset",
+       .id = UCLASS_RESET,
+       .of_match = syscon_reset_ids,
+       .probe = syscon_reset_probe,
+       .priv_auto_alloc_size = sizeof(struct syscon_reset_priv),
+       .ops = &syscon_reset_ops,
+};
diff --git a/include/configs/sipeed-maix.h b/include/configs/sipeed-maix.h
new file mode 100644 (file)
index 0000000..a46473f
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef CONFIGS_SIPEED_MAIX_H
+#define CONFIGS_SIPEED_MAIX_H
+
+#include <linux/sizes.h>
+
+#define CONFIG_SYS_LOAD_ADDR 0x80000000
+/* Start just below the second bank so we don't clobber it during reloc */
+#define CONFIG_SYS_INIT_SP_ADDR 0x803FFFFF
+#define CONFIG_SYS_MALLOC_LEN SZ_128K
+#define CONFIG_SYS_CACHELINE_SIZE 64
+
+#define CONFIG_SYS_SDRAM_BASE 0x80000000
+/* Don't relocate into AI ram since it isn't set up yet */
+#define CONFIG_SYS_SDRAM_SIZE (SZ_4M + SZ_2M)
+
+/* For early init */
+#define K210_SYSCTL_BASE 0x50440000
+
+#endif /* CONFIGS_SIPEED_MAIX_H */
index 3711386..f02ec95 100644 (file)
@@ -799,7 +799,9 @@ static inline fdt_addr_t dev_read_addr(const struct udevice *dev)
 
 static inline void *dev_read_addr_ptr(const struct udevice *dev)
 {
-       return devfdt_get_addr_ptr(dev);
+       void *addr = devfdt_get_addr_ptr(dev);
+
+       return ((fdt_addr_t)(uintptr_t)addr == FDT_ADDR_T_NONE) ? NULL : addr;
 }
 
 static inline fdt_addr_t dev_read_addr_pci(const struct udevice *dev)
diff --git a/include/dt-bindings/clock/k210-sysctl.h b/include/dt-bindings/clock/k210-sysctl.h
new file mode 100644 (file)
index 0000000..0e3ed3f
--- /dev/null
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef CLOCK_K210_SYSCTL_H
+#define CLOCK_K210_SYSCTL_H
+
+/*
+ * Arbitrary identifiers for clocks.
+ */
+#define K210_CLK_NONE   0
+#define K210_CLK_IN0_H  1
+#define K210_CLK_PLL0_H 2
+#define K210_CLK_PLL0   3
+#define K210_CLK_PLL1   4
+#define K210_CLK_PLL2   5
+#define K210_CLK_PLL2_H 6
+#define K210_CLK_CPU    7
+#define K210_CLK_SRAM0  8
+#define K210_CLK_SRAM1  9
+#define K210_CLK_APB0   10
+#define K210_CLK_APB1   11
+#define K210_CLK_APB2   12
+#define K210_CLK_ROM    13
+#define K210_CLK_DMA    14
+#define K210_CLK_AI     15
+#define K210_CLK_DVP    16
+#define K210_CLK_FFT    17
+#define K210_CLK_GPIO   18
+#define K210_CLK_SPI0   19
+#define K210_CLK_SPI1   20
+#define K210_CLK_SPI2   21
+#define K210_CLK_SPI3   22
+#define K210_CLK_I2S0   23
+#define K210_CLK_I2S1   24
+#define K210_CLK_I2S2   25
+#define K210_CLK_I2S0_M 26
+#define K210_CLK_I2S1_M 27
+#define K210_CLK_I2S2_M 28
+#define K210_CLK_I2C0   29
+#define K210_CLK_I2C1   30
+#define K210_CLK_I2C2   31
+#define K210_CLK_UART1  32
+#define K210_CLK_UART2  33
+#define K210_CLK_UART3  34
+#define K210_CLK_AES    35
+#define K210_CLK_FPIOA  36
+#define K210_CLK_TIMER0 37
+#define K210_CLK_TIMER1 38
+#define K210_CLK_TIMER2 39
+#define K210_CLK_WDT0   40
+#define K210_CLK_WDT1   41
+#define K210_CLK_SHA    42
+#define K210_CLK_OTP    43
+#define K210_CLK_RTC    44
+#define K210_CLK_ACLK   45
+
+#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 (file)
index 0000000..bfc918d
--- /dev/null
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef K210_SYSCTL_H
+#define K210_SYSCTL_H
+
+/* Taken from kendryte-standalone-sdk/lib/drivers/include/sysctl.h */
+#define K210_SYSCTL_GIT_ID     0x00 /* Git short commit id */
+#define K210_SYSCTL_UART_BAUD  0x04 /* Default UARTHS baud rate */
+#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 controller 0 */
+#define K210_SYSCTL_SEL1       0x24 /* Clock select controller 1 */
+#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 0 */
+#define K210_SYSCTL_DMA_SEL1   0x68 /* DMA handshake selector 1 */
+#define K210_SYSCTL_POWER_SEL  0x6C /* IO Power Mode Select controller */
+
+#endif /* 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 (file)
index 0000000..12bb388
--- /dev/null
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019 Sean Anderson <seanga2@gmail.com>
+ */
+
+#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 */
diff --git a/include/kendryte/bypass.h b/include/kendryte/bypass.h
new file mode 100644 (file)
index 0000000..a081cbd
--- /dev/null
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+#ifndef K210_BYPASS_H
+#define K210_BYPASS_H
+
+#include <clk.h>
+
+struct k210_bypass {
+       struct clk clk;
+       struct clk **children; /* Clocks to reparent */
+       struct clk **saved_parents; /* Parents saved over en-/dis-able */
+       struct clk *bypassee; /* Clock to bypass */
+       const struct clk_ops *bypassee_ops; /* Ops of the bypass clock */
+       struct clk *alt; /* Clock to set children to when bypassing */
+       size_t child_count;
+};
+
+#define to_k210_bypass(_clk) container_of(_clk, struct k210_bypass, clk)
+
+int k210_bypass_set_children(struct clk *clk, struct clk **children,
+                            size_t child_count);
+struct clk *k210_register_bypass_struct(const char *name,
+                                       const char *parent_name,
+                                       struct k210_bypass *bypass);
+struct clk *k210_register_bypass(const char *name, const char *parent_name,
+                                struct clk *bypassee,
+                                const struct clk_ops *bypassee_ops,
+                                struct clk *alt);
+#endif /* K210_BYPASS_H */
diff --git a/include/kendryte/clk.h b/include/kendryte/clk.h
new file mode 100644 (file)
index 0000000..9c6245d
--- /dev/null
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef K210_CLK_H
+#define K210_CLK_H
+
+#define LOG_CATEGORY UCLASS_CLK
+#include <linux/types.h>
+#include <linux/clk-provider.h>
+
+static inline struct clk *k210_clk_gate(const char *name,
+                                       const char *parent_name,
+                                       void __iomem *reg, u8 bit_idx)
+{
+       return clk_register_gate(NULL, name, parent_name, 0, reg, bit_idx, 0,
+                                NULL);
+}
+
+static inline struct clk *k210_clk_half(const char *name,
+                                       const char *parent_name)
+{
+       return clk_register_fixed_factor(NULL, name, parent_name, 0, 1, 2);
+}
+
+static inline struct clk *k210_clk_div(const char *name,
+                                      const char *parent_name,
+                                      void __iomem *reg, u8 shift, u8 width)
+{
+       return clk_register_divider(NULL, name, parent_name, 0, reg, shift,
+                                   width, 0);
+}
+
+#endif /* K210_CLK_H */
diff --git a/include/kendryte/pll.h b/include/kendryte/pll.h
new file mode 100644 (file)
index 0000000..c8e3200
--- /dev/null
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+#ifndef K210_PLL_H
+#define K210_PLL_H
+
+#include <clk.h>
+#include <test/export.h>
+
+#define K210_PLL_CLKR GENMASK(3, 0)
+#define K210_PLL_CLKF GENMASK(9, 4)
+#define K210_PLL_CLKOD GENMASK(13, 10) /* Output Divider */
+#define K210_PLL_BWADJ GENMASK(19, 14) /* BandWidth Adjust */
+#define K210_PLL_RESET BIT(20)
+#define K210_PLL_PWRD BIT(21) /* PoWeReD */
+#define K210_PLL_INTFB BIT(22) /* Internal FeedBack */
+#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 width; /* Width of lock bits to test against */
+};
+
+#define to_k210_pll(_clk) container_of(_clk, struct k210_pll, clk)
+
+struct k210_pll_config {
+       u8 r;
+       u8 f;
+       u8 od;
+};
+
+#ifdef CONFIG_UNIT_TEST
+TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in,
+                                    struct k210_pll_config *best);
+#define nop()
+#endif
+
+extern const struct clk_ops k210_pll_ops;
+
+struct clk *k210_register_pll_struct(const char *name, const char *parent_name,
+                                    struct k210_pll *pll);
+struct clk *k210_register_pll(const char *name, const char *parent_name,
+                             void __iomem *reg, void __iomem *lock, u8 shift,
+                             u8 width);
+
+#endif /* K210_PLL_H */
diff --git a/include/test/export.h b/include/test/export.h
new file mode 100644 (file)
index 0000000..afc755a
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef TEST_EXPORT_H
+#define TEST_EXPORT_H
+
+/* Declare something static, unless we are doing unit tests */
+#ifdef CONFIG_UNIT_TEST
+#define TEST_STATIC
+#else
+#define TEST_STATIC static
+#endif
+
+#endif /* TEST_EXPORT_H */
index b96dbe1..7b6781b 100644 (file)
@@ -110,8 +110,10 @@ int hcreate_r(size_t nel, struct hsearch_data *htab)
        }
 
        /* There is still another table active. Return with error. */
-       if (htab->table != NULL)
+       if (htab->table != NULL) {
+               __set_errno(EINVAL);
                return 0;
+       }
 
        /* Change nel to the first prime number not smaller as nel. */
        nel |= 1;               /* make odd */
@@ -124,8 +126,10 @@ int hcreate_r(size_t nel, struct hsearch_data *htab)
        /* allocate memory and zero out */
        htab->table = (struct env_entry_node *)calloc(htab->size + 1,
                                                sizeof(struct env_entry_node));
-       if (htab->table == NULL)
+       if (htab->table == NULL) {
+               __set_errno(ENOMEM);
                return 0;
+       }
 
        /* everything went alright */
        return 1;
index 6c18fd0..0d1c66f 100644 (file)
@@ -73,4 +73,7 @@ obj-$(CONFIG_DMA) += dma.o
 obj-$(CONFIG_DM_MDIO) += mdio.o
 obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o
 obj-$(CONFIG_DM_RNG) += rng.o
+obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o
+obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
+obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o
 endif
diff --git a/test/dm/k210_pll.c b/test/dm/k210_pll.c
new file mode 100644 (file)
index 0000000..54764f2
--- /dev/null
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+/* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */
+#include <div64.h>
+#include <dm/test.h>
+#include <kendryte/pll.h>
+#include <test/ut.h>
+
+static int dm_test_k210_pll_calc_config(u32 rate, u32 rate_in,
+                                       struct k210_pll_config *best)
+{
+       u64 f, r, od, max_r, inv_ratio;
+       s64 error, best_error;
+
+       best_error = S64_MAX;
+       error = best_error;
+       max_r = min(16ULL, DIV_ROUND_DOWN_ULL(rate_in, 13300000));
+       inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate);
+
+       /* Brute force it */
+       for (r = 1; r <= max_r; r++) {
+               for (f = 1; f <= 64; f++) {
+                       for (od = 1; od <= 16; od++) {
+                               u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r);
+
+                               if (vco > 1750000000 || vco < 340000000)
+                                       continue;
+
+                               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;
+                               }
+                       }
+               }
+       }
+
+       if (best_error == S64_MAX)
+               return -EINVAL;
+       return 0;
+}
+
+static int dm_test_k210_pll_compare(struct k210_pll_config *ours,
+                                   struct k210_pll_config *theirs)
+{
+       return (u32)ours->f * theirs->r * theirs->od !=
+              (u32)theirs->f * ours->r * ours->od;
+}
+
+static int dm_test_k210_pll(struct unit_test_state *uts)
+{
+       struct k210_pll_config ours, theirs;
+
+       /* General range checks */
+       ut_asserteq(-EINVAL, k210_pll_calc_config(0, 26000000, &theirs));
+       ut_asserteq(-EINVAL, k210_pll_calc_config(390000000, 0, &theirs));
+       ut_asserteq(-EINVAL, k210_pll_calc_config(2000000000, 26000000,
+                                                 &theirs));
+       ut_asserteq(-EINVAL, k210_pll_calc_config(390000000, 2000000000,
+                                                 &theirs));
+       ut_asserteq(-EINVAL, k210_pll_calc_config(1500000000, 20000000,
+                                                 &theirs));
+
+       /* Verify we get the same output with brute-force */
+       ut_assertok(dm_test_k210_pll_calc_config(390000000, 26000000, &ours));
+       ut_assertok(k210_pll_calc_config(390000000, 26000000, &theirs));
+       ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+       ut_assertok(dm_test_k210_pll_calc_config(26000000, 390000000, &ours));
+       ut_assertok(k210_pll_calc_config(26000000, 390000000, &theirs));
+       ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+       ut_assertok(dm_test_k210_pll_calc_config(400000000, 26000000, &ours));
+       ut_assertok(k210_pll_calc_config(400000000, 26000000, &theirs));
+       ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+       ut_assertok(dm_test_k210_pll_calc_config(27000000, 26000000, &ours));
+       ut_assertok(k210_pll_calc_config(27000000, 26000000, &theirs));
+       ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+       ut_assertok(dm_test_k210_pll_calc_config(26000000, 27000000, &ours));
+       ut_assertok(k210_pll_calc_config(26000000, 27000000, &theirs));
+       ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+       return 0;
+}
+DM_TEST(dm_test_k210_pll, 0);
diff --git a/test/dm/simple-pm-bus.c b/test/dm/simple-pm-bus.c
new file mode 100644 (file)
index 0000000..978c7f1
--- /dev/null
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <dm/device-internal.h>
+#include <test/ut.h>
+#include <asm/clk.h>
+#include <asm/power-domain.h>
+
+/* These must match the ids in the device tree */
+#define TEST_CLOCK_ID 4
+#define TEST_POWER_ID 1
+
+static int dm_test_simple_pm_bus(struct unit_test_state *uts)
+{
+       struct udevice *power;
+       struct udevice *clock;
+       struct udevice *bus;
+
+       ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN,
+                                             "power-domain", &power));
+       ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-sbox",
+                                             &clock));
+       ut_asserteq(0, sandbox_power_domain_query(power, TEST_POWER_ID));
+       ut_asserteq(0, sandbox_clk_query_enable(clock, TEST_CLOCK_ID));
+
+       ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, "pm-bus-test",
+                                             &bus));
+       ut_asserteq(1, sandbox_power_domain_query(power, TEST_POWER_ID));
+       ut_asserteq(1, sandbox_clk_query_enable(clock, TEST_CLOCK_ID));
+
+       ut_assertok(device_remove(bus, DM_REMOVE_NORMAL));
+       /* must re-probe since device_remove also removes the power domain */
+       ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN,
+                                             "power-domain", &power));
+       ut_asserteq(0, sandbox_power_domain_query(power, TEST_POWER_ID));
+       ut_asserteq(0, sandbox_clk_query_enable(clock, TEST_CLOCK_ID));
+
+       return 0;
+}
+DM_TEST(dm_test_simple_pm_bus, DM_TESTF_SCAN_FDT);
diff --git a/test/dm/syscon-reset.c b/test/dm/syscon-reset.c
new file mode 100644 (file)
index 0000000..edabdb2
--- /dev/null
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <regmap.h>
+#include <reset.h>
+#include <syscon.h>
+#include <test/ut.h>
+#include <asm/test.h>
+#include <linux/bitops.h>
+
+/* The following values must match the device tree */
+#define TEST_RESET_REG 1
+#define TEST_RESET_ASSERT_HIGH 0
+#define TEST_RESET_ASSERT (TEST_RESET_ASSERT_HIGH ? (u32)-1 : (u32)0)
+#define TEST_RESET_DEASSERT (~TEST_RESET_ASSERT)
+
+#define TEST_RESET_VALID 15
+#define TEST_RESET_NOMASK 30
+#define TEST_RESET_OUTOFRANGE 60
+
+static int dm_test_syscon_reset(struct unit_test_state *uts)
+{
+       struct regmap *map;
+       struct reset_ctl rst;
+       struct udevice *reset;
+       struct udevice *syscon;
+       struct udevice *syscon_reset;
+       uint reg;
+
+       ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "syscon-reset-test",
+                                             &reset));
+       ut_assertok(uclass_get_device_by_name(UCLASS_SYSCON, "syscon@0",
+                                             &syscon));
+       ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "syscon-reset",
+                                             &syscon_reset));
+       ut_assertok_ptr((map = syscon_get_regmap(syscon)));
+
+       ut_asserteq(-EINVAL, reset_get_by_name(reset, "no_mask", &rst));
+       ut_asserteq(-EINVAL, reset_get_by_name(reset, "out_of_range", &rst));
+       ut_assertok(reset_get_by_name(reset, "valid", &rst));
+
+       sandbox_set_enable_memio(true);
+       ut_assertok(regmap_write(map, TEST_RESET_REG, TEST_RESET_DEASSERT));
+       ut_assertok(reset_assert(&rst));
+       ut_assertok(regmap_read(map, TEST_RESET_REG, &reg));
+       ut_asserteq(TEST_RESET_DEASSERT ^ BIT(TEST_RESET_VALID), reg);
+
+       ut_assertok(reset_deassert(&rst));
+       ut_assertok(regmap_read(map, TEST_RESET_REG, &reg));
+       ut_asserteq(TEST_RESET_DEASSERT, reg);
+
+       return 0;
+}
+DM_TEST(dm_test_syscon_reset, DM_TESTF_SCAN_FDT);