mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-25 13:14:19 +08:00
Merge branch 'next' of https://gitlab.denx.de/u-boot/custodians/u-boot-riscv into next
- Add Sipeed Maix support - Update clock handler and proper cpu features
This commit is contained in:
commit
785195941b
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -8,6 +8,10 @@
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
cpu1 = &cpu1;
|
||||
cpu2 = &cpu2;
|
||||
cpu3 = &cpu3;
|
||||
cpu4 = &cpu4;
|
||||
spi0 = &qspi0;
|
||||
spi2 = &qspi2;
|
||||
};
|
||||
|
47
arch/riscv/dts/k210-maix-bit.dts
Normal file
47
arch/riscv/dts/k210-maix-bit.dts
Normal file
@ -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>;
|
||||
};
|
594
arch/riscv/dts/k210.dtsi
Normal file
594
arch/riscv/dts/k210.dtsi
Normal file
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -15,7 +15,11 @@
|
||||
#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)
|
||||
@ -29,6 +33,22 @@
|
||||
#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))
|
||||
@ -88,17 +110,35 @@
|
||||
#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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -30,20 +30,6 @@
|
||||
#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_init_ipi(void)
|
||||
{
|
||||
long *ret = syscon_get_first_range(RISCV_SYSCON_PLIC);
|
||||
|
||||
if (IS_ERR(ret))
|
||||
return PTR_ERR(ret);
|
||||
gd->arch.plic = ret;
|
||||
|
||||
return init_plic();
|
||||
}
|
||||
|
||||
int riscv_send_ipi(int hart)
|
||||
{
|
||||
unsigned int ipi;
|
||||
unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
|
||||
|
||||
PLIC_BASE_GET();
|
||||
|
||||
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));
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -24,22 +24,8 @@
|
||||
|
||||
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_init_ipi(void)
|
||||
{
|
||||
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)
|
||||
{
|
||||
CLINT_BASE_GET();
|
||||
|
||||
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;
|
||||
|
@ -12,38 +12,6 @@
|
||||
|
||||
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;
|
||||
struct ipi_data ipi = {
|
||||
.addr = addr,
|
||||
.arg0 = arg0,
|
||||
.arg1 = arg1,
|
||||
};
|
||||
|
||||
ipi.addr = addr;
|
||||
ipi.arg0 = arg0;
|
||||
ipi.arg1 = arg1;
|
||||
|
||||
ret = send_ipi_many(&ipi, wait);
|
||||
|
||||
return ret;
|
||||
return send_ipi_many(&ipi, wait);
|
||||
}
|
||||
|
@ -1038,6 +1038,27 @@
|
||||
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"
|
||||
|
@ -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,
|
||||
};
|
||||
|
47
board/sipeed/maix/Kconfig
Normal file
47
board/sipeed/maix/Kconfig
Normal file
@ -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
|
11
board/sipeed/maix/MAINTAINERS
Normal file
11
board/sipeed/maix/MAINTAINERS
Normal file
@ -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
|
5
board/sipeed/maix/Makefile
Normal file
5
board/sipeed/maix/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (c) 2019 Western Digital Corporation or its affiliates.
|
||||
|
||||
obj-y += maix.o
|
41
board/sipeed/maix/maix.c
Normal file
41
board/sipeed/maix/maix.c
Normal file
@ -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;
|
||||
}
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
|
8
configs/sipeed_maix_bitm_defconfig
Normal file
8
configs/sipeed_maix_bitm_defconfig
Normal file
@ -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
|
@ -18,6 +18,7 @@ Board-specific doc
|
||||
renesas/index
|
||||
rockchip/index
|
||||
sifive/index
|
||||
sipeed/index
|
||||
st/index
|
||||
tbs/index
|
||||
toradex/index
|
||||
|
9
doc/board/sipeed/index.rst
Normal file
9
doc/board/sipeed/index.rst
Normal file
@ -0,0 +1,9 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
Sipeed
|
||||
======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
maix
|
298
doc/board/sipeed/maix.rst
Normal file
298
doc/board/sipeed/maix.rst
Normal file
@ -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
|
||||
========== ========= ===========
|
44
doc/device-tree-bindings/bus/simple-pm-bus.txt
Normal file
44
doc/device-tree-bindings/bus/simple-pm-bus.txt
Normal file
@ -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>;
|
||||
};
|
33
doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
Normal file
33
doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
Normal file
@ -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>;
|
||||
};
|
||||
};
|
36
doc/device-tree-bindings/reset/syscon-reset.txt
Normal file
36
doc/device-tree-bindings/reset/syscon-reset.txt
Normal file
@ -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>;
|
||||
};
|
@ -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):
|
||||
==================
|
||||
|
@ -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"
|
||||
|
@ -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/
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)));
|
||||
|
12
drivers/clk/kendryte/Kconfig
Normal file
12
drivers/clk/kendryte/Kconfig
Normal file
@ -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.
|
1
drivers/clk/kendryte/Makefile
Normal file
1
drivers/clk/kendryte/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-y += bypass.o clk.o pll.o
|
270
drivers/clk/kendryte/bypass.c
Normal file
270
drivers/clk/kendryte/bypass.c
Normal file
@ -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,
|
||||
};
|
663
drivers/clk/kendryte/clk.c
Normal file
663
drivers/clk/kendryte/clk.c
Normal file
@ -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,
|
||||
};
|
601
drivers/clk/kendryte/pll.c
Normal file
601
drivers/clk/kendryte/pll.c
Normal file
@ -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,
|
||||
};
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
56
drivers/core/simple-pm-bus.c
Normal file
56
drivers/core/simple-pm-bus.c
Normal file
@ -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,
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
dev_read_u32(dev, "clock-frequency", (u32 *)&info->cpu_freq);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
81
drivers/reset/reset-syscon.c
Normal file
81
drivers/reset/reset-syscon.c
Normal file
@ -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,
|
||||
};
|
24
include/configs/sipeed-maix.h
Normal file
24
include/configs/sipeed-maix.h
Normal file
@ -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 */
|
@ -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)
|
||||
|
59
include/dt-bindings/clock/k210-sysctl.h
Normal file
59
include/dt-bindings/clock/k210-sysctl.h
Normal file
@ -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 */
|
38
include/dt-bindings/mfd/k210-sysctl.h
Normal file
38
include/dt-bindings/mfd/k210-sysctl.h
Normal file
@ -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 */
|
38
include/dt-bindings/reset/k210-sysctl.h
Normal file
38
include/dt-bindings/reset/k210-sysctl.h
Normal file
@ -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 */
|
31
include/kendryte/bypass.h
Normal file
31
include/kendryte/bypass.h
Normal file
@ -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 */
|
35
include/kendryte/clk.h
Normal file
35
include/kendryte/clk.h
Normal file
@ -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 */
|
57
include/kendryte/pll.h
Normal file
57
include/kendryte/pll.h
Normal file
@ -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 */
|
16
include/test/export.h
Normal file
16
include/test/export.h
Normal file
@ -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 */
|
@ -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;
|
||||
|
@ -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
|
||||
|
96
test/dm/k210_pll.c
Normal file
96
test/dm/k210_pll.c
Normal file
@ -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);
|
45
test/dm/simple-pm-bus.c
Normal file
45
test/dm/simple-pm-bus.c
Normal file
@ -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);
|
59
test/dm/syscon-reset.c
Normal file
59
test/dm/syscon-reset.c
Normal file
@ -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, ®));
|
||||
ut_asserteq(TEST_RESET_DEASSERT ^ BIT(TEST_RESET_VALID), reg);
|
||||
|
||||
ut_assertok(reset_deassert(&rst));
|
||||
ut_assertok(regmap_read(map, TEST_RESET_REG, ®));
|
||||
ut_asserteq(TEST_RESET_DEASSERT, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_syscon_reset, DM_TESTF_SCAN_FDT);
|
Loading…
Reference in New Issue
Block a user