CI: https://git.u-boot-project.org/u-boot/custodians/u-boot-riscv/-/pipelines/676

- Adds support for the SpacemiT K1 and
- Updates the MAINTAINERS.
This commit is contained in:
Tom Rini
2026-07-21 09:06:07 -06:00
67 changed files with 6864 additions and 1270 deletions
+2 -2
View File
@@ -1608,8 +1608,8 @@ T: git https://git.u-boot-project.org/u-boot/custodians/u-boot-nand-flash.git
F: drivers/mtd/nand/raw/
RISC-V
M: Rick Chen <rick@andestech.com>
M: Leo <ycliang@andestech.com>
M: Tim Ouyang <tim609@andestech.com>
M: Leo Liang <leo.liang@sifive.com>
S: Maintained
T: git https://git.u-boot-project.org/u-boot/custodians/u-boot-riscv.git
F: arch/riscv/
+4 -4
View File
@@ -14,9 +14,6 @@ config TARGET_ANDES_AE350
config TARGET_ANDES_VOYAGER
bool "Support Andes Voyager Board"
config TARGET_BANANAPI_F3
bool "Support BananaPi F3 Board"
config TARGET_BEAGLEBOARD_BEAGLEVFIRE
bool "Support BeagleBoard BeagleV-Fire Board (based on Microchip MPFS)"
@@ -50,6 +47,9 @@ config TARGET_SIPEED_MAIX
bool "Support Sipeed Maix Board"
select SYS_CACHE_SHIFT_6
config TARGET_SPACEMIT_K1
bool "Support Spacemit K1 SoC"
config TARGET_STARFIVE_VISIONFIVE2
bool "Support StarFive VisionFive2 Board"
select BOARD_LATE_INIT
@@ -119,7 +119,7 @@ source "board/sifive/unmatched/Kconfig"
source "board/sipeed/maix/Kconfig"
source "board/sophgo/milkv_duo/Kconfig"
source "board/sophgo/licheerv_nano/Kconfig"
source "board/spacemit/bananapi-f3/Kconfig"
source "board/spacemit/k1/Kconfig"
source "board/starfive/visionfive2/Kconfig"
source "board/thead/th1520_lpi4a/Kconfig"
source "board/xilinx/mbv/Kconfig"
+6
View File
@@ -1,12 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2024, Kongyang Liu <seashell11234455@gmail.com>
# Copyright (C) 2025-2026, RISCstar Ltd.
if TARGET_SPACEMIT_K1
config SPACEMIT_K1
bool
select BINMAN
select ARCH_EARLY_INIT_R
select SYS_CACHE_SHIFT_6
select SUPPORT_SPL
imply CPU
imply CPU_RISCV
imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
@@ -17,3 +21,5 @@ config SPACEMIT_K1
imply SPL_CPU
imply SPL_OPENSBI
imply SPL_LOAD_FIT
endif
-1
View File
@@ -2,7 +2,6 @@
dtb-$(CONFIG_TARGET_ANDES_AE350) += ae350_32.dtb ae350_64.dtb
dtb-$(CONFIG_TARGET_ANDES_VOYAGER) += qilai-voyager.dtb
dtb-$(CONFIG_TARGET_BANANAPI_F3) += k1-bananapi-f3.dtb
dtb-$(CONFIG_TARGET_K230_CANMV) += k230-canmv.dtb
dtb-$(CONFIG_TARGET_MICROCHIP_ICICLE) += mpfs-icicle-kit.dtb
dtb-$(CONFIG_TARGET_MILKV_DUO) += cv1800b-milkv-duo.dtb
-2
View File
@@ -93,9 +93,7 @@
configurations {
#ifndef CONFIG_MULTI_DTB_FIT
default = "conf-1";
#endif
#if !defined(CONFIG_OF_BOARD) || defined(CONFIG_MULTI_DTB_FIT)
@conf-SEQ {
+113
View File
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0+ OR MIT
/*
* Copyright (C) 2026 RISCstar Ltd.
*/
#include "binman.dtsi"
/ {
memory@0 {
device_type = "memory";
reg = <0x00000000 0x00000000 0x00000000 0x80000000>;
};
};
&uart0 {
bootph-pre-ram;
};
&osc_32k {
bootph-pre-ram;
};
&vctcxo_1m {
bootph-pre-ram;
};
&vctcxo_3m {
bootph-pre-ram;
};
&vctcxo_24m {
bootph-pre-ram;
};
&syscon_mpmu {
bootph-pre-ram;
};
&pll {
bootph-pre-ram;
};
&syscon_apmu {
bootph-pre-ram;
};
&syscon_apbc {
bootph-pre-ram;
};
&i2c2 {
bootph-pre-ram;
resets = <&syscon_apbc RESET_TWSI2>;
eeprom@50 {
bootph-pre-ram;
};
};
/*
* The kernel pmic@41 nodes use the standard regulator-* properties only;
* U-Boot's regulator framework looks up suppliers by `regulator-name`.
* Carry the U-Boot-specific names (vdd_core / vdd_1v8 / vdd_1v8_mmc) here
* so SPL can program the bucks that DDR training depends on.
*/
&i2c8 {
bootph-pre-ram;
resets = <&syscon_apbc RESET_TWSI8>;
pmic@41 {
bootph-pre-ram;
regulators {
buck1 {
regulator-name = "vdd_core";
bootph-pre-ram;
};
buck3 {
regulator-name = "vdd_1v8";
bootph-pre-ram;
};
aldo1 {
regulator-name = "vdd_1v8_mmc";
bootph-pre-ram;
};
};
};
};
&binman {
u-boot-spl-ddr {
type = "section";
filename = "u-boot-spl-ddr.bin";
pad-byte = <0xff>;
u-boot-spl {
};
ddr-fw {
type = "blob";
filename = "ddr_fw.bin";
align = <64>;
};
u-boot-any {
type = "section";
size = <0>;
offset = <0>;
};
};
};
-28
View File
@@ -1,28 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
/*
* Copyright (C) 2024 Yangyu Chen <cyy@cyyself.name>
*/
#include "k1.dtsi"
#include "binman.dtsi"
#include "k1-pinctrl.dtsi"
/ {
model = "Banana Pi BPI-F3";
compatible = "bananapi,bpi-f3", "spacemit,k1";
chosen {
stdout-path = "serial0";
};
memory@0 {
device_type = "memory";
reg = <0x00000000 0x00000000 0x00000000 0x80000000>;
};
};
&uart0 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_2_cfg>;
status = "okay";
};
+273
View File
@@ -0,0 +1,273 @@
// SPDX-License-Identifier: GPL-2.0+ OR MIT
/*
* Copyright (C) 2026 RISCstar Ltd.
*/
#include <dt-bindings/clock/spacemit,k1-syscon.h>
#include "binman.dtsi"
/ {
aliases {
console = &uart0;
serial0 = &uart0;
};
chosen {
stdout-path = "serial0:115200n8";
};
memory@0 {
device_type = "memory";
reg = <0x00000000 0x00000000 0x00000000 0x80000000>;
};
};
&syscon_mpmu {
clocks = <&osc_32k>, <&vctcxo_1m>, <&vctcxo_3m>, <&vctcxo_24m>,
<&pll CLK_PLL1_D4>;
clock-names = "osc", "vctcxo_1m", "vctcxo_3m", "vctcxo_24m",
"pll1_d4";
};
&syscon_apbc {
clocks = <&osc_32k>, <&vctcxo_1m>, <&vctcxo_3m>, <&vctcxo_24m>,
<&pll CLK_PLL1_D4>,
<&syscon_mpmu CLK_PLL1_31P5>,
<&syscon_apmu CLK_PMUA_ACLK>;
clock-names = "osc", "vctcxo_1m", "vctcxo_3m", "vctcxo_24m",
"pll1_d4", "pll1_d78_31p5", "pmua_aclk";
};
&uart0 {
bootph-pre-ram;
};
&osc_32k {
bootph-pre-ram;
};
&vctcxo_1m {
bootph-pre-ram;
};
&vctcxo_3m {
bootph-pre-ram;
};
&vctcxo_24m {
bootph-pre-ram;
};
&syscon_mpmu {
bootph-pre-ram;
};
&pll {
bootph-pre-ram;
};
&syscon_apmu {
bootph-pre-ram;
};
&syscon_apbc {
bootph-pre-ram;
};
&i2c2 {
bootph-pre-ram;
status = "okay";
pinctrl-0 = <&i2c2_0_cfg>;
pinctrl-names = "default";
resets = <&syscon_apbc RESET_TWSI2>;
eeprom@50 {
bootph-pre-ram;
status = "okay";
compatible = "atmel,24c02";
reg = <0x50>;
vcc-supply = <&buck3_1v8>; /* EEPROM_VCC1V8 */
pagesize = <16>;
read-only;
size = <256>;
nvmem-layout {
compatible = "onie,tlv-layout";
mac-address {
#nvmem-cell-cells = <1>;
};
num-macs {
};
serial-number {
};
};
};
};
&i2c8 {
bootph-pre-ram;
pinctrl-0 = <&i2c8_cfg>;
pinctrl-names = "default";
resets = <&syscon_apbc RESET_TWSI8>;
status = "okay";
pmic@41 {
bootph-pre-ram;
compatible = "spacemit,p1";
reg = <0x41>;
interrupts = <64>;
status = "okay";
regulators {
buck1 {
bootph-pre-ram;
regulator-name = "vdd_core";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3450000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck2 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3450000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck3_1v8: buck3 {
bootph-pre-ram;
regulator-name = "vdd_1v8";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1800000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck4 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3300000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck5 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3450000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
buck6 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3450000>;
regulator-ramp-delay = <5000>;
regulator-always-on;
};
aldo1 {
bootph-pre-ram;
regulator-name = "vdd_1v8_mmc";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
regulator-boot-on;
};
aldo2 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
};
aldo3 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
};
aldo4 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
};
dldo1 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
regulator-boot-on;
};
dldo2 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
};
dldo3 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
};
dldo4 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
regulator-always-on;
};
dldo5 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
};
dldo6 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
regulator-always-on;
};
dldo7 {
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3400000>;
};
};
};
};
&qspi {
bootph-pre-ram;
pinctrl-names = "default";
pinctrl-0 = <&qspi_cfg>;
status = "okay";
flash@0 {
bootph-pre-ram;
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <26500000>;
m25p,fast-read;
broken-flash-reset;
status = "okay";
};
};
&binman {
u-boot-spl-ddr {
type = "section";
filename = "u-boot-spl-ddr.bin";
pad-byte = <0xff>;
u-boot-spl {
};
ddr-fw {
type = "blob";
filename = "ddr_fw.bin";
align = <64>;
};
u-boot-any {
type = "section";
size = <0>;
offset = <0>;
};
};
};
-19
View File
@@ -1,19 +0,0 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (C) 2022 Spacemit Inc.
* Copyright (C) 2025 Yixun Lan <dlan@gentoo.org>
*/
#define K1_PADCONF(pin, func) (((pin) << 16) | (func))
&pinctrl {
uart0_2_cfg: uart0-2-cfg {
uart0-2-pins {
pinmux = <K1_PADCONF(68, 2)>,
<K1_PADCONF(69, 2)>;
bias-pull-up = <0>;
drive-strength = <32>;
};
};
};
-480
View File
@@ -1,480 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
/*
* Copyright (C) 2024 Yangyu Chen <cyy@cyyself.name>
*/
/dts-v1/;
/ {
#address-cells = <2>;
#size-cells = <2>;
model = "SpacemiT K1";
compatible = "spacemit,k1";
aliases {
serial0 = &uart0;
serial1 = &uart2;
serial2 = &uart3;
serial3 = &uart4;
serial4 = &uart5;
serial5 = &uart6;
serial6 = &uart7;
serial7 = &uart8;
serial8 = &uart9;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
timebase-frequency = <24000000>;
cpu-map {
cluster0 {
core0 {
cpu = <&cpu_0>;
};
core1 {
cpu = <&cpu_1>;
};
core2 {
cpu = <&cpu_2>;
};
core3 {
cpu = <&cpu_3>;
};
};
cluster1 {
core0 {
cpu = <&cpu_4>;
};
core1 {
cpu = <&cpu_5>;
};
core2 {
cpu = <&cpu_6>;
};
core3 {
cpu = <&cpu_7>;
};
};
};
cpu_0: cpu@0 {
compatible = "spacemit,x60", "riscv";
device_type = "cpu";
reg = <0>;
riscv,isa = "rv64imafdcv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt";
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "v", "zicbom",
"zicbop", "zicboz", "zicntr", "zicond", "zicsr",
"zifencei", "zihintpause", "zihpm", "zfh", "zba",
"zbb", "zbc", "zbs", "zkt", "zvfh", "zvkt",
"sscofpmf", "sstc", "svinval", "svnapot", "svpbmt";
riscv,cbom-block-size = <64>;
riscv,cbop-block-size = <64>;
riscv,cboz-block-size = <64>;
i-cache-block-size = <64>;
i-cache-size = <32768>;
i-cache-sets = <128>;
d-cache-block-size = <64>;
d-cache-size = <32768>;
d-cache-sets = <128>;
next-level-cache = <&cluster0_l2_cache>;
mmu-type = "riscv,sv39";
cpu0_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
interrupt-controller;
#interrupt-cells = <1>;
};
};
cpu_1: cpu@1 {
compatible = "spacemit,x60", "riscv";
device_type = "cpu";
reg = <1>;
riscv,isa = "rv64imafdcv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt";
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "v", "zicbom",
"zicbop", "zicboz", "zicntr", "zicond", "zicsr",
"zifencei", "zihintpause", "zihpm", "zfh", "zba",
"zbb", "zbc", "zbs", "zkt", "zvfh", "zvkt",
"sscofpmf", "sstc", "svinval", "svnapot", "svpbmt";
riscv,cbom-block-size = <64>;
riscv,cbop-block-size = <64>;
riscv,cboz-block-size = <64>;
i-cache-block-size = <64>;
i-cache-size = <32768>;
i-cache-sets = <128>;
d-cache-block-size = <64>;
d-cache-size = <32768>;
d-cache-sets = <128>;
next-level-cache = <&cluster0_l2_cache>;
mmu-type = "riscv,sv39";
cpu1_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
interrupt-controller;
#interrupt-cells = <1>;
};
};
cpu_2: cpu@2 {
compatible = "spacemit,x60", "riscv";
device_type = "cpu";
reg = <2>;
riscv,isa = "rv64imafdcv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt";
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "v", "zicbom",
"zicbop", "zicboz", "zicntr", "zicond", "zicsr",
"zifencei", "zihintpause", "zihpm", "zfh", "zba",
"zbb", "zbc", "zbs", "zkt", "zvfh", "zvkt",
"sscofpmf", "sstc", "svinval", "svnapot", "svpbmt";
riscv,cbom-block-size = <64>;
riscv,cbop-block-size = <64>;
riscv,cboz-block-size = <64>;
i-cache-block-size = <64>;
i-cache-size = <32768>;
i-cache-sets = <128>;
d-cache-block-size = <64>;
d-cache-size = <32768>;
d-cache-sets = <128>;
next-level-cache = <&cluster0_l2_cache>;
mmu-type = "riscv,sv39";
cpu2_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
interrupt-controller;
#interrupt-cells = <1>;
};
};
cpu_3: cpu@3 {
compatible = "spacemit,x60", "riscv";
device_type = "cpu";
reg = <3>;
riscv,isa = "rv64imafdcv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt";
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "v", "zicbom",
"zicbop", "zicboz", "zicntr", "zicond", "zicsr",
"zifencei", "zihintpause", "zihpm", "zfh", "zba",
"zbb", "zbc", "zbs", "zkt", "zvfh", "zvkt",
"sscofpmf", "sstc", "svinval", "svnapot", "svpbmt";
riscv,cbom-block-size = <64>;
riscv,cbop-block-size = <64>;
riscv,cboz-block-size = <64>;
i-cache-block-size = <64>;
i-cache-size = <32768>;
i-cache-sets = <128>;
d-cache-block-size = <64>;
d-cache-size = <32768>;
d-cache-sets = <128>;
next-level-cache = <&cluster0_l2_cache>;
mmu-type = "riscv,sv39";
cpu3_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
interrupt-controller;
#interrupt-cells = <1>;
};
};
cpu_4: cpu@4 {
compatible = "spacemit,x60", "riscv";
device_type = "cpu";
reg = <4>;
riscv,isa = "rv64imafdcv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt";
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "v", "zicbom",
"zicbop", "zicboz", "zicntr", "zicond", "zicsr",
"zifencei", "zihintpause", "zihpm", "zfh", "zba",
"zbb", "zbc", "zbs", "zkt", "zvfh", "zvkt",
"sscofpmf", "sstc", "svinval", "svnapot", "svpbmt";
riscv,cbom-block-size = <64>;
riscv,cbop-block-size = <64>;
riscv,cboz-block-size = <64>;
i-cache-block-size = <64>;
i-cache-size = <32768>;
i-cache-sets = <128>;
d-cache-block-size = <64>;
d-cache-size = <32768>;
d-cache-sets = <128>;
next-level-cache = <&cluster1_l2_cache>;
mmu-type = "riscv,sv39";
cpu4_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
interrupt-controller;
#interrupt-cells = <1>;
};
};
cpu_5: cpu@5 {
compatible = "spacemit,x60", "riscv";
device_type = "cpu";
reg = <5>;
riscv,isa = "rv64imafdcv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt";
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "v", "zicbom",
"zicbop", "zicboz", "zicntr", "zicond", "zicsr",
"zifencei", "zihintpause", "zihpm", "zfh", "zba",
"zbb", "zbc", "zbs", "zkt", "zvfh", "zvkt",
"sscofpmf", "sstc", "svinval", "svnapot", "svpbmt";
riscv,cbom-block-size = <64>;
riscv,cbop-block-size = <64>;
riscv,cboz-block-size = <64>;
i-cache-block-size = <64>;
i-cache-size = <32768>;
i-cache-sets = <128>;
d-cache-block-size = <64>;
d-cache-size = <32768>;
d-cache-sets = <128>;
next-level-cache = <&cluster1_l2_cache>;
mmu-type = "riscv,sv39";
cpu5_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
interrupt-controller;
#interrupt-cells = <1>;
};
};
cpu_6: cpu@6 {
compatible = "spacemit,x60", "riscv";
device_type = "cpu";
reg = <6>;
riscv,isa = "rv64imafdcv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt";
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "v", "zicbom",
"zicbop", "zicboz", "zicntr", "zicond", "zicsr",
"zifencei", "zihintpause", "zihpm", "zfh", "zba",
"zbb", "zbc", "zbs", "zkt", "zvfh", "zvkt",
"sscofpmf", "sstc", "svinval", "svnapot", "svpbmt";
riscv,cbom-block-size = <64>;
riscv,cbop-block-size = <64>;
riscv,cboz-block-size = <64>;
i-cache-block-size = <64>;
i-cache-size = <32768>;
i-cache-sets = <128>;
d-cache-block-size = <64>;
d-cache-size = <32768>;
d-cache-sets = <128>;
next-level-cache = <&cluster1_l2_cache>;
mmu-type = "riscv,sv39";
cpu6_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
interrupt-controller;
#interrupt-cells = <1>;
};
};
cpu_7: cpu@7 {
compatible = "spacemit,x60", "riscv";
device_type = "cpu";
reg = <7>;
riscv,isa = "rv64imafdcv_zicbom_zicbop_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zba_zbb_zbc_zbs_zkt_zvfh_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt";
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "v", "zicbom",
"zicbop", "zicboz", "zicntr", "zicond", "zicsr",
"zifencei", "zihintpause", "zihpm", "zfh", "zba",
"zbb", "zbc", "zbs", "zkt", "zvfh", "zvkt",
"sscofpmf", "sstc", "svinval", "svnapot", "svpbmt";
riscv,cbom-block-size = <64>;
riscv,cbop-block-size = <64>;
riscv,cboz-block-size = <64>;
i-cache-block-size = <64>;
i-cache-size = <32768>;
i-cache-sets = <128>;
d-cache-block-size = <64>;
d-cache-size = <32768>;
d-cache-sets = <128>;
next-level-cache = <&cluster1_l2_cache>;
mmu-type = "riscv,sv39";
cpu7_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
interrupt-controller;
#interrupt-cells = <1>;
};
};
cluster0_l2_cache: l2-cache0 {
compatible = "cache";
cache-block-size = <64>;
cache-level = <2>;
cache-size = <524288>;
cache-sets = <512>;
cache-unified;
};
cluster1_l2_cache: l2-cache1 {
compatible = "cache";
cache-block-size = <64>;
cache-level = <2>;
cache-size = <524288>;
cache-sets = <512>;
cache-unified;
};
};
soc {
compatible = "simple-bus";
interrupt-parent = <&plic>;
#address-cells = <2>;
#size-cells = <2>;
dma-noncoherent;
ranges;
uart0: serial@d4017000 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xd4017000 0x0 0x100>;
interrupts = <42>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
};
uart2: serial@d4017100 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xd4017100 0x0 0x100>;
interrupts = <44>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
};
uart3: serial@d4017200 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xd4017200 0x0 0x100>;
interrupts = <45>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
};
uart4: serial@d4017300 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xd4017300 0x0 0x100>;
interrupts = <46>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
};
uart5: serial@d4017400 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xd4017400 0x0 0x100>;
interrupts = <47>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
};
uart6: serial@d4017500 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xd4017500 0x0 0x100>;
interrupts = <48>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
};
uart7: serial@d4017600 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xd4017600 0x0 0x100>;
interrupts = <49>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
};
uart8: serial@d4017700 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xd4017700 0x0 0x100>;
interrupts = <50>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
};
uart9: serial@d4017800 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xd4017800 0x0 0x100>;
interrupts = <51>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
};
plic: interrupt-controller@e0000000 {
compatible = "spacemit,k1-plic", "sifive,plic-1.0.0";
reg = <0x0 0xe0000000 0x0 0x4000000>;
interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 9>,
<&cpu1_intc 11>, <&cpu1_intc 9>,
<&cpu2_intc 11>, <&cpu2_intc 9>,
<&cpu3_intc 11>, <&cpu3_intc 9>,
<&cpu4_intc 11>, <&cpu4_intc 9>,
<&cpu5_intc 11>, <&cpu5_intc 9>,
<&cpu6_intc 11>, <&cpu6_intc 9>,
<&cpu7_intc 11>, <&cpu7_intc 9>;
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
riscv,ndev = <159>;
};
clint: timer@e4000000 {
compatible = "spacemit,k1-clint", "sifive,clint0";
reg = <0x0 0xe4000000 0x0 0x10000>;
interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>,
<&cpu1_intc 3>, <&cpu1_intc 7>,
<&cpu2_intc 3>, <&cpu2_intc 7>,
<&cpu3_intc 3>, <&cpu3_intc 7>,
<&cpu4_intc 3>, <&cpu4_intc 7>,
<&cpu5_intc 3>, <&cpu5_intc 7>,
<&cpu6_intc 3>, <&cpu6_intc 7>,
<&cpu7_intc 3>, <&cpu7_intc 7>;
};
sec_uart1: serial@f0612000 {
compatible = "spacemit,k1-uart", "intel,xscale-uart";
reg = <0x0 0xf0612000 0x0 0x100>;
interrupts = <43>;
clock-frequency = <14857000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "reserved"; /* for TEE usage */
};
reset: reset-controller@d4050000 {
compatible = "spacemit,k1-reset";
reg = <0x0 0xd4050000 0x0 0x209c>,
<0x0 0xd4282800 0x0 0x400>,
<0x0 0xd4015000 0x0 0x1000>,
<0x0 0xd4090000 0x0 0x1000>,
<0x0 0xd4282c00 0x0 0x400>,
<0x0 0xd8440000 0x0 0x98>,
<0x0 0xc0000000 0x0 0x4280>,
<0x0 0xf0610000 0x0 0x20>;
reg-names = "mpmu", "apmu", "apbc", "apbs", "ciu", "dciu", "ddrc", "apbc2";
#reset-cells = <1>;
status = "disabled";
};
pinctrl: pinctrl@d401e000 {
compatible = "spacemit,k1-pinctrl", "pinctrl-single";
reg = <0x0 0xd401e000 0x0 0x400>;
pinctrl-single,register-width = <32>;
};
};
};
-6
View File
@@ -1,6 +0,0 @@
BananaPi F3
M: Huan Zhou <pericycle.cc@@gmail.com>
S: Maintained
F: board/spacemit/bananapi-f3/
F: configs/bananapi-f3_defconfig
F: doc/board/spacemit/bananapi-f3.rst
-5
View File
@@ -1,5 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (c) 2024, Kongyang Liu <seashell11234455@gmail.com>
obj-y := board.o
@@ -1,7 +1,7 @@
if TARGET_BANANAPI_F3
if TARGET_SPACEMIT_K1
config SYS_BOARD
default "bananapi-f3"
default "k1"
config SYS_VENDOR
default "spacemit"
@@ -10,7 +10,7 @@ config SYS_CPU
default "k1"
config SYS_CONFIG_NAME
default "bananapi-f3"
default "k1"
config TEXT_BASE
default 0x00200000
+14
View File
@@ -0,0 +1,14 @@
BananaPi F3
M: Huan Zhou <pericycle.cc@gmail.com>
M: Guodong Xu <guodong.xu@riscstar.com>
L: u-boot-spacemit@groups.io
S: Maintained
F: arch/riscv/dts/k1-*-u-boot.dtsi
F: board/spacemit/k1/
F: configs/spacemit_k1_defconfig
F: doc/board/spacemit/bananapi-f3.rst
F: drivers/gpio/spacemit_gpio.c
F: drivers/i2c/k1_i2c.c
F: drivers/pinctrl/spacemit/
F: drivers/power/pmic/pmic_spacemit_p1.c
F: drivers/power/regulator/spacemit_p1_regulator.c
+28
View File
@@ -0,0 +1,28 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (c) 2024, Kongyang Liu <seashell11234455@gmail.com>
# Copyright (c) 2025-2026, RISCstar Ltd.
obj-y := board.o
obj-$(CONFIG_SPL_BUILD) += spl.o
DDR_FW_SRC ?= $(DDR_FW_FILE)
FW_TARGET = $(objtree)/ddr_fw.bin
$(obj)/spl.o: $(FW_TARGET)
$(FW_TARGET): FORCE
@if [ -n "$(DDR_FW_SRC)" ] && [ -f "$(DDR_FW_SRC)" ]; then \
if ! cmp -s "$(DDR_FW_SRC)" $@ 2>/dev/null; then \
echo " Copying DDR firmware from: $(DDR_FW_SRC)"; \
cp "$(DDR_FW_SRC)" $@; \
fi; \
elif [ -s $@ ]; then \
: ; \
else \
echo " Note: No DDR firmware found, creating empty placeholder"; \
echo " (Set DDR_FW_FILE to specify firmware location)"; \
touch $@; \
fi
clean-files += $(FW_TARGET)
+391
View File
@@ -0,0 +1,391 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2025-2026, RISCstar Ltd.
*/
#include <asm/io.h>
#include <binman.h>
#include <binman_sym.h>
#include <clk.h>
#include <clk-uclass.h>
#include <cpu_func.h>
#include <configs/k1.h>
#include <dm/device.h>
#include <dm/uclass.h>
#include <i2c.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <log.h>
#include <power/regulator.h>
#include <spi_flash.h>
#include <spl.h>
#include <tlv_eeprom.h>
#include "tlv_codes.h"
#define MUX_MODE4 4
#define EDGE_NONE BIT(6)
#define PULL_UP (6 << 13) /* bit[15:13] 110 */
#define PAD_DS_MEDIUM BIT(12)
#define PAD_1V8_DS2 PAD_DS_MEDIUM
#define I2C_PIN_CONFIG(x) ((x) | EDGE_NONE | PULL_UP | PAD_1V8_DS2)
#define I2C_BUF_SIZE 64
#define MFP_GPIO_84 0xd401e154
#define MFP_GPIO_85 0xd401e158
#define DDR_FIRMWARE_BASE 0xc082d000
#define DDR_DEFAULT_CS_NUM 2
#define DDR_DEFAULT_TYPE "LPDDR4X"
#define DDR_DEFAULT_TX_ODT 80
#define DDR_DEFAULT_DATA_RATE 2400
#define MAGIC_NUM 0xaa55aa55
typedef void (*puts_func_t)(const char *s);
typedef int (*ddr_init_func_t)(u64 ddr_base, u32 cs_num, u32 data_rate,
puts_func_t puts);
struct ddr_cfg {
u32 data_rate;
u32 cs_num;
u32 tx_odt;
u8 type[I2C_BUF_SIZE];
};
binman_sym_declare(ulong, ddr_fw, image_pos);
binman_sym_declare(ulong, ddr_fw, size);
char product_name[I2C_BUF_SIZE] = "k1";
static void i2c_early_init(void)
{
struct udevice *bus;
// eeprom: I2C2, pin group(GPIO_84, GPIO_85)
writel(I2C_PIN_CONFIG(MUX_MODE4), (void __iomem *)MFP_GPIO_84);
writel(I2C_PIN_CONFIG(MUX_MODE4), (void __iomem *)MFP_GPIO_85);
udelay(100);
uclass_first_device(UCLASS_I2C, &bus);
while (bus) {
uclass_next_device(&bus);
if (!bus)
break;
}
}
int read_product_name(char *name, int size)
{
u8 eeprom_data[TLV_TOTAL_LEN_MAX], *p;
struct tlvinfo_header *tlv_hdr;
struct tlvinfo_tlv *tlv_entry;
int ret, i = 0;
u32 entry_size;
if (!name || size <= 0)
return -EINVAL;
ret = read_tlvinfo_tlv_eeprom(eeprom_data, &tlv_hdr,
&tlv_entry, i);
if (ret)
return ret;
p = (u8 *)tlv_entry;
for (i = 0; i < tlv_hdr->totallen; ) {
if (tlv_entry->type == TLV_CODE_PRODUCT_NAME) {
if (tlv_entry->length < size)
size = tlv_entry->length;
memset(name, 0, size);
memcpy(name, &tlv_entry->value[0], size);
return 0;
}
if (tlv_entry->type == TLV_CODE_CRC_32)
return -ENOENT;
entry_size = tlv_entry->length + sizeof(struct tlvinfo_tlv);
i += entry_size;
p += entry_size;
tlv_entry = (struct tlvinfo_tlv *)p;
}
return -ENOENT;
}
static void clk_early_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_name(UCLASS_CLK, "clock-controller@d4090000", &dev);
if (ret)
panic("Fail to detect clock-controller@d4090000\n");
ret = uclass_get_device_by_name(UCLASS_CLK, "system-controller@d4050000", &dev);
if (ret)
panic("Fail to detect system-controller@d4050000\n");
ret = uclass_get_device_by_name(UCLASS_CLK, "system-controller@d4282800", &dev);
if (ret)
panic("Fail to detect system-controller@d4282800\n");
ret = uclass_get_device_by_name(UCLASS_CLK, "system-controller@d4015000", &dev);
if (ret)
panic("Fail to detect system-controller@d4015000\n");
if (device_active(dev))
log_debug("clk: device is active\n");
else
log_debug("clk: device not active, probing...\n");
}
void serial_early_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device(UCLASS_SERIAL, 0, &dev);
if (ret)
panic("Serial uclass init failed: %d\n", ret);
}
static void set_vdd_core(void)
{
struct udevice *dev;
int ret;
ret = regulator_get_by_platname("vdd_core", &dev);
if (ret)
panic("Fail to detect vdd_core (%d)\n", ret);
ret = regulator_set_enable(dev, true);
if (ret)
log_warning("Fail to enable vdd_core (%d)\n", ret);
ret = regulator_get_value(dev);
if (ret < 0)
log_warning("Fail to read vdd_core (%d)\n", ret);
log_info("vdd_core, value:%d\n", ret);
}
static void set_vdd_1v8(void)
{
struct udevice *dev;
int ret;
ret = regulator_get_by_platname("vdd_1v8", &dev);
if (ret)
panic("Fail to detect vdd_1v8 (%d)\n", ret);
ret = regulator_set_value(dev, 1800000);
if (ret)
log_warning("Fail to set vdd_1v8 as 1800000 (%d)\n", ret);
ret = regulator_set_enable(dev, true);
if (ret)
log_warning("Fail to enable vdd_1v8 (%d)\n", ret);
ret = regulator_get_value(dev);
if (ret < 0)
log_warning("Fail to read vdd_1v8 (%d)\n", ret);
log_info("vdd_1v8, value:%d\n", ret);
}
static void set_vdd_mmc(void)
{
struct udevice *dev;
int ret;
ret = regulator_get_by_platname("vdd_1v8_mmc", &dev);
if (ret)
panic("Fail to detect vdd_1v8_mmc (%d)\n", ret);
ret = regulator_set_value(dev, 1800000);
if (ret)
log_warning("Fail to set vdd_1v8_mmc as 1800000 (%d)\n", ret);
ret = regulator_set_enable(dev, true);
if (ret)
log_warning("Fail to enable vdd_1v8_mmc (%d)\n", ret);
ret = regulator_get_value(dev);
if (ret < 0)
log_warning("Fail to read vdd_1v8_mmc (%d)\n", ret);
log_info("vdd_1v8_mmc, value:%d\n", ret);
}
void pmic_init(void)
{
struct udevice *pmic_dev;
int ret;
ret = uclass_get_device(UCLASS_PMIC, 0, &pmic_dev);
if (ret)
panic("Fail to detect PMIC (%d)\n", ret);
set_vdd_core();
set_vdd_1v8();
set_vdd_mmc();
}
/* Set default value for DDR chips */
static void ddr_cfg_init(struct ddr_cfg *cfg)
{
memset(cfg, 0, sizeof(struct ddr_cfg));
cfg->data_rate = DDR_DEFAULT_DATA_RATE;
cfg->cs_num = DDR_DEFAULT_CS_NUM;
cfg->tx_odt = DDR_DEFAULT_TX_ODT;
strcpy(cfg->type, DDR_DEFAULT_TYPE);
}
int read_ddr_info(struct ddr_cfg *cfg)
{
u8 eeprom_data[TLV_TOTAL_LEN_MAX], *p;
struct tlvinfo_header *tlv_hdr;
struct tlvinfo_tlv *tlv_entry;
u32 size, entry_size;
int ret, i;
bool found = false;
if (!cfg)
return -EINVAL;
ddr_cfg_init(cfg);
ret = read_tlvinfo_tlv_eeprom(eeprom_data, &tlv_hdr,
&tlv_entry, i);
if (ret)
return ret;
p = (u8 *)tlv_entry;
for (i = 0; i < tlv_hdr->totallen; ) {
switch (tlv_entry->type) {
case TLV_CODE_DDR_CSNUM:
memcpy(&cfg->cs_num, &tlv_entry->value[0], 1);
found = true;
break;
case TLV_CODE_DDR_TYPE:
size = min((u32)tlv_entry->length, (u32)I2C_BUF_SIZE);
memcpy(&cfg->type[0], &tlv_entry->value[0], size);
found = true;
break;
case TLV_CODE_DDR_DATARATE:
memcpy(&cfg->data_rate, &tlv_entry->value[0], 2);
found = true;
break;
case TLV_CODE_DDR_TX_ODT:
memcpy(&cfg->tx_odt, &tlv_entry->value[0], 1);
found = true;
break;
case TLV_CODE_CRC_32:
if (!found)
return -ENOENT;
return 0;
}
entry_size = tlv_entry->length + sizeof(struct tlvinfo_tlv);
i += entry_size;
p += entry_size;
tlv_entry = (struct tlvinfo_tlv *)p;
}
if (!found)
return -ENOENT;
return 0;
}
void ddr_early_init(void)
{
void __iomem *src, *dst;
ulong pos, size;
struct ddr_cfg cfg;
ddr_init_func_t ddr_init;
pos = binman_sym(ulong, ddr_fw, image_pos);
size = binman_sym(ulong, ddr_fw, size);
src = (void __iomem *)pos;
dst = (void __iomem *)(DDR_FIRMWARE_BASE);
log_info("DDR firmware: [0x%lx]:0x%x, size:0x%lx\n", pos, readl(src), size);
memcpy((u8 *)dst, (u8 *)src, size);
size = round_up(size, 64);
/*
* Ensure the just-written DDR firmware bytes are observable in the
* instruction stream. RISC-V's fence.i alone is sufficient for the
* single-hart SPL case; skip flush_dcache_range as cbo.flush may
* trap on our current privilege state.
*/
asm volatile ("fence.i" ::: "memory");
read_ddr_info(&cfg);
ddr_init = (ddr_init_func_t)DDR_FIRMWARE_BASE;
#ifdef DEBUG
ddr_init(0xc0000000, cfg.cs_num, cfg.data_rate, puts);
#else
ddr_init(0xc0000000, cfg.cs_num, cfg.data_rate, NULL);
#endif
writel(MAGIC_NUM, (void __iomem *)0x00000000);
flush_dcache_range(0, 64);
invalidate_dcache_range(0, 64);
if (readl((void __iomem *)0x00000000) == MAGIC_NUM)
log_info("DDR is ready\n");
else
log_info("DDR is not ready\n");
}
void nor_early_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device(UCLASS_SPI, 0, &dev);
if (ret)
panic("Fail to detect spi controller.\n");
udelay(10);
ret = uclass_get_device(UCLASS_SPI_FLASH, 0, &dev);
if (ret)
log_info("Fail to detect spi nor flash.\n");
udelay(10);
}
void board_init_f(ulong dummy)
{
int ret;
ret = spl_early_init();
if (ret)
panic("spl_early_init() failed:%d\n", ret);
riscv_cpu_setup();
clk_early_init();
serial_early_init();
preloader_console_init();
i2c_early_init();
ret = read_product_name(product_name, I2C_BUF_SIZE);
if (ret)
log_info("Fail to detect board:%d\n", ret);
else
log_info("Get board name:%s\n", product_name);
pmic_init();
ddr_early_init();
nor_early_init();
}
u32 spl_boot_device(void)
{
return BOOT_DEVICE_SPI;
}
void spl_board_init(void)
{
}
int board_fit_config_name_match(const char *name)
{
char fdt_name[I2C_BUF_SIZE];
int i;
memset(fdt_name, 0, I2C_BUF_SIZE);
if (!strncmp(product_name, "k1-x_", 5)) {
snprintf(fdt_name, I2C_BUF_SIZE, "%s-%s", "k1",
&product_name[5]);
}
if (fdt_name[0] == '\0') {
/* set default board name */
sprintf(fdt_name, "k1-musepi-pro");
}
for (i = 0; i < I2C_BUF_SIZE; i++) {
if (fdt_name[i] == '\0')
break;
fdt_name[i] = tolower(fdt_name[i]);
}
if (!strcmp(name, fdt_name))
return 0;
return -ENOENT;
}
void *board_spl_fit_buffer_addr(ulong fit_size, int sectors, int bl_len)
{
return (void *)CONFIG_SPL_LOAD_FIT_ADDRESS;
}
+22
View File
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2025-2026 RISCstar Ltd.
*/
#ifndef __TLV_CODES_H
#define __TLV_CODES_H
#define TLV_CODE_SDK_VERSION 0x40
#define TLV_CODE_DDR_CSNUM 0x41
#define TLV_CODE_DDR_TYPE 0x42
#define TLV_CODE_DDR_DATARATE 0x43
#define TLV_CODE_DDR_TX_ODT 0x44
#define TLV_CODE_WIFI_MAC_ADDR 0x60
#define TLV_CODE_BLUETOOTH_ADDR 0x61
#define TLV_CODE_PMIC_TYPE 0x80
#define TLV_CODE_EEPROM_I2C_INDEX 0x81
#define TLV_CODE_EEPROM_PIN_GROUP 0x82
#endif /* __TLV_CODES_H */
-24
View File
@@ -1,24 +0,0 @@
CONFIG_RISCV=y
CONFIG_SYS_MALLOC_LEN=0x1000000
CONFIG_NR_DRAM_BANKS=2
CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x1000000
CONFIG_DEFAULT_DEVICE_TREE="k1-bananapi-f3"
CONFIG_SYS_BOOTM_LEN=0xa000000
CONFIG_SYS_LOAD_ADDR=0x200000
CONFIG_TARGET_BANANAPI_F3=y
CONFIG_ARCH_RV64I=y
CONFIG_RISCV_SMODE=y
CONFIG_FIT=y
CONFIG_SUPPORT_RAW_INITRD=y
CONFIG_OF_BOARD_SETUP=y
CONFIG_SYS_CBSIZE=256
CONFIG_SYS_PBSIZE=276
# CONFIG_BOARD_INIT is not set
CONFIG_HUSH_PARSER=y
CONFIG_ENV_OVERWRITE=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_SINGLE=y
CONFIG_RESET_SPACEMIT_K1=y
CONFIG_SYS_NS16550=y
CONFIG_SYS_NS16550_MEM32=y
+95
View File
@@ -0,0 +1,95 @@
CONFIG_RISCV=y
CONFIG_SYS_MALLOC_LEN=0x1000000
CONFIG_SYS_MALLOC_F_LEN=0x10000
CONFIG_NR_DRAM_BANKS=2
CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x1000000
CONFIG_DEFAULT_DEVICE_TREE="spacemit/k1-musepi-pro"
CONFIG_OF_LIST="spacemit/k1-bananapi-f3 spacemit/k1-musepi-pro"
CONFIG_SPL_SYS_MALLOC_F_LEN=0x4000
CONFIG_SPL_STACK=0xc083fb00
CONFIG_SPL_TEXT_BASE=0xc0801000
CONFIG_SPL_BSS_START_ADDR=0xc083fc00
CONFIG_SPL_BSS_MAX_SIZE=0x400
CONFIG_SPL_STACK_R=y
CONFIG_SPL_STACK_R_ADDR=0xc00000
CONFIG_SPL_SYS_MALLOC_SIMPLE=y
CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x100000
CONFIG_SYS_BOOTM_LEN=0xa000000
CONFIG_SYS_LOAD_ADDR=0x200000
CONFIG_SPL_SIZE_LIMIT=0x31000
CONFIG_SPL=y
CONFIG_DEBUG_UART_BASE=0xd4017000
CONFIG_DEBUG_UART_CLOCK=14700000
CONFIG_TARGET_SPACEMIT_K1=y
CONFIG_ARCH_RV64I=y
CONFIG_RISCV_SMODE=y
CONFIG_SPL_RISCV_MMODE=y
# CONFIG_SPL_SMP is not set
CONFIG_STACK_SIZE=0x100000
CONFIG_FIT=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_SPL_HAS_LOAD_FIT_ADDRESS=y
CONFIG_SPL_LOAD_FIT_ADDRESS=0x08000000
CONFIG_SUPPORT_RAW_INITRD=y
CONFIG_OF_BOARD_SETUP=y
CONFIG_SYS_CBSIZE=256
CONFIG_SYS_PBSIZE=276
# CONFIG_BOARD_INIT is not set
CONFIG_SPL_MAX_SIZE=0x33000
# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set
CONFIG_SPL_HAVE_INIT_STACK=y
CONFIG_SPL_I2C=y
CONFIG_SPL_DM_RESET=y
CONFIG_SPL_POWER=y
CONFIG_HUSH_PARSER=y
CONFIG_CMD_TLV_EEPROM=y
CONFIG_SPL_CMD_TLV_EEPROM=y
CONFIG_OF_UPSTREAM=y
CONFIG_ENV_OVERWRITE=y
CONFIG_SPL_REGMAP=y
CONFIG_SPL_SYSCON=y
CONFIG_CLK=y
CONFIG_CLK_CCF=y
CONFIG_SPL_CLK=y
CONFIG_SPL_CLK_CCF=y
CONFIG_CLK_SPACEMIT=y
CONFIG_CLK_SPACEMIT_K1=y
CONFIG_DM_I2C=y
CONFIG_SYS_I2C_SPACEMIT_K1=y
CONFIG_MISC=y
CONFIG_I2C_EEPROM=y
CONFIG_SPL_I2C_EEPROM=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_SPACEMIT_K1=y
CONFIG_GPIO=y
CONFIG_DM_GPIO=y
CONFIG_CMD_GPIO=y
CONFIG_SPACEMIT_GPIO=y
CONFIG_DM_PMIC=y
CONFIG_PMIC_SPACEMIT_P1=y
CONFIG_DM_REGULATOR=y
CONFIG_SPL_DM_REGULATOR=y
CONFIG_DM_REGULATOR_SPACEMIT_P1=y
CONFIG_DEBUG_UART_NS16550=y
CONFIG_DEBUG_UART_SHIFT=2
CONFIG_DEBUG_UART_ANNOUNCE=y
CONFIG_SYS_NS16550=y
CONFIG_SYS_NS16550_MEM32=y
CONFIG_TIMER_EARLY=y
CONFIG_SPL_LIB_RATIONAL=y
CONFIG_SPI=y
CONFIG_DM_SPI=y
CONFIG_DM_SPI_FLASH=y
CONFIG_FSL_QSPI=y
CONFIG_SPI_FLASH=y
CONFIG_SPI_FLASH_WINBOND=y
CONFIG_SPI_MEM=y
CONFIG_SYS_SPI_U_BOOT_OFFS=0x000a0000
CONFIG_CMD_SPI=y
CONFIG_SPL_SPI=y
CONFIG_SPL_DM_SPI=y
CONFIG_SPL_DM_SPI_FLASH=y
CONFIG_SPL_SPI_LOAD=y
CONFIG_SPL_SPI_FLASH_TINY=y
CONFIG_SPL_SPI_FLASH_SUPPORT=y
+1 -1
View File
@@ -29,7 +29,7 @@ built for SpacemiT K1 SoC as below:
.. code-block:: console
cd <U-Boot-dir>
make bananapi-f3_defconfig
make spacemit_k1_defconfig
make OPENSBI=<OpenSBI-dir>/build/platform/generic/firmware/fw_dynamic.bin
This will generate u-boot.itb
+1
View File
@@ -6,4 +6,5 @@ SpacemiT
:maxdepth: 1
bananapi-f3
k1-spl
+243
View File
@@ -0,0 +1,243 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
SpacemiT K1 SPL Build and Test Guide
=====================================
This guide explains how to build and test U-Boot SPL on SpacemiT K1 based
boards. It covers building SPL with DDR initialization, generating the signed
FSBL image, and deploying via USB fastboot.
Tested boards: Banana Pi BPI-F3, MusePi Pro.
.. note::
The procedure described here loads SPL into SRAM via USB fastboot and
does not modify the on-board flash, so a power cycle restores the
board's normal boot path.
At this stage of the patchset, SPL initializes DDR and then halts with
"SPL: Unsupported Boot Device!" because SPI NOR storage support is not
yet available in U-Boot proper. This document will be updated when
U-Boot stage support is ready.
Prerequisites
~~~~~~~~~~~~~
- A SpacemiT K1 board with USB Type-C and UART access
- USB-to-UART adapter (3.3V TTL)
- ``minicom`` or equivalent serial terminal, configured at 115200 8N1
- ``fastboot`` tool on the host
Hardware Setup
~~~~~~~~~~~~~~
**1. UART Connection**
Remove all other cables first, then attach UART. Connect a 3.3V
USB-to-UART cable to the board's UART header (J25 on the BPI-F3)::
BPI-F3 top view
+--------------------------------------------------+
| |
| J15: USB-C [====] [FDL] [PWR] [RST] |
| |
| |
| J25 (UART header) |
| [TXD] [RXD] [GND] |
+--------------------------------------------------+
After UART is connected, attach the USB Type-C cable to the OTG port
(J15 on BPI-F3) to power on.
**2. Serial Console**
.. code-block:: console
$ minicom -D /dev/ttyUSB0
Default baudrate: 115200.
Building U-Boot SPL
~~~~~~~~~~~~~~~~~~~~
**1. Obtain the DDR training firmware**
The DDR training firmware is a proprietary binary provided by SpacemiT. It is
not included in U-Boot and must be downloaded separately from:
https://github.com/spacemit-com/spacemit-firmware/tree/master/k1/v0.2
Download ``ddr_fw.bin`` from that directory.
This binary is integrated into the SPL image at build time via the binman
framework. When the SPL image is loaded to SRAM (e.g., via USB fastboot),
the SPL executes the DDR firmware from SRAM to perform DDR initialization.
**2. Obtain OpenSBI fw_dynamic.bin**
Any pre-built ``fw_dynamic.bin`` is sufficient. Upstream OpenSBI
sources are at https://github.com/riscv-software-src/opensbi if you
need to build one.
At this stage of the patchset, SPL halts before invoking OpenSBI,
but U-Boot's binman still packages ``fw_dynamic.bin`` into
``u-boot.itb`` and the build fails if it is missing.
**3. Build SPL**
.. code-block:: console
$ export CROSS_COMPILE=riscv64-linux-gnu-
$ export ARCH=riscv
$ export DDR_FW_FILE=$(pwd)/ddr_fw.bin
$ export OPENSBI=/path/to/fw_dynamic.bin
$ make spacemit_k1_defconfig
$ make
Output: ``u-boot-spl-ddr.bin`` in the build directory. This image contains the
SPL code and the DDR firmware blob packaged together via binman.
.. note::
If ``DDR_FW_FILE`` is not set, the build completes with an empty
placeholder. The resulting SPL will boot but cannot initialize DDR.
**4. Generate signed FSBL image**
The K1 BootROM requires a signed first-stage bootloader (FSBL). The signing
tool (``tools/build_binary_file.py``) is in SpacemiT's vendor U-Boot repository:
.. code-block:: console
$ git clone -b k1-bl-v2.2.y https://gitee.com/spacemit-buildroot/uboot-2022.10
The script uses ``fsbl_ddr.json`` which may not exist by default. If
``fsbl_ddr.json`` does not exist in
``uboot-2022.10/board/spacemit/k1-x/configs/``,
create it by copying ``fsbl.json`` and replacing the reference to
``u-boot-spl.bin`` with ``u-boot-spl-ddr.bin``:
.. code-block:: console
$ cd uboot-2022.10/board/spacemit/k1-x/configs
$ cp fsbl.json fsbl_ddr.json
$ sed -i 's/u-boot-spl\.bin/u-boot-spl-ddr.bin/g' fsbl_ddr.json
Create the ``fsbl.sh`` script below in the ``uboot-2022.10`` directory.
Update the path variables to match your local setup:
.. code-block:: bash
#!/bin/sh
MAINLINE_UBOOT_IMG_PATH="{your path}/u-boot"
MAINLINE_SPL_IMG_PATH="{your path}/u-boot/spl"
FSBL_PATH="{your path}/uboot-2022.10/spl_bin"
KEY_TOOL_PATH="{your path}/uboot-2022.10/tools"
CONFIG_PATH="{your path}/uboot-2022.10/board/spacemit/k1-x/configs"
mkdir -p ${FSBL_PATH}
echo "Clean binaries in ${FSBL_PATH}"
rm -f ${FSBL_PATH}/u-boot-spl-ddr.bin
rm -f ${FSBL_PATH}/u-boot-spl.bin
if [ ! -d ${MAINLINE_SPL_IMG_PATH} ]; then
MAINLINE_UBOOT_IMG_PATH="{your path}/build"
MAINLINE_SPL_IMG_PATH="{your path}/build/spl"
fi
# The signing tool reads u-boot-spl-ddr.bin from the path declared in
# fsbl_ddr.json's "source" field, which is resolved relative to the
# JSON file's directory -- i.e. one level up from CONFIG_PATH, not
# FSBL_PATH. Stage the unsigned binary there before signing.
cp ${MAINLINE_UBOOT_IMG_PATH}/u-boot-spl-ddr.bin ${CONFIG_PATH}/../
python3 ${KEY_TOOL_PATH}/build_binary_file.py \
-c ${CONFIG_PATH}/fsbl_ddr.json \
-o ${FSBL_PATH}/FSBL.bin
Then run:
.. code-block:: console
$ chmod +x fsbl.sh
$ ./fsbl.sh
Output: ``FSBL.bin`` in the ``spl_bin`` directory, ready for deployment.
Deploying via USB Fastboot
~~~~~~~~~~~~~~~~~~~~~~~~~~~
To enter BootROM fastboot mode:
1. Power off the board by unplugging its power supply.
2. **Press and hold** the FDL button (called "Boot Key" on some boards;
see the board layout above for the BPI-F3).
3. While holding the button, use a USB cable to connect the OTG port to
your host. This cable is also used by fastboot to upload the firmware.
4. Release the button.
On the host, ``fastboot devices`` should list the board::
dfu-device DFU download
The serial console shows the BootROM's USB download handler trace,
including a line like::
usb2d_initialize : enter
This indicates the board is ready to accept an image via USB.
.. tip::
If you are worried about insufficient USB power, you can first plug
in the power, then release the button, and then plug in the USB
cable.
On the host:
.. code-block:: console
$ sudo fastboot stage FSBL.bin
$ sudo fastboot continue
Expected Output
~~~~~~~~~~~~~~~~
During successful SPL boot with DDR initialization, the serial console
shows output like::
<debug_uart>
U-Boot SPL 2026.04-rc4-00430-g8b84b1ed8ea9 (May 08 2026 - 09:08:27 -0400)
Fail to detect board:-19
vdd_core, value:900000
vdd_1v8, value:1800000
vdd_1v8_mmc, value:1800000
DDR firmware: [0xc0815840]:0xf0227179, size:0x8d98
DDR is ready
SPL: Unsupported Boot Device!
SPL: failed to boot from all boot devices
### ERROR ### Please RESET the board ###
To walk through the key lines:
- ``DDR firmware: [...], size:0x8d98`` - DDR firmware loaded successfully
- ``DDR is ready`` - DDR initialization completed
- ``SPL: failed to boot from all boot devices`` - expected at this stage,
confirms that SPL with DDR init is working correctly
If SPL hangs before printing DDR messages, verify that ``DDR_FW_FILE`` was set
during build and that ``ddr_fw.bin`` is not empty.
.. tip::
To see verbose DDR training output from the DDR firmware (per-phase
training pass/fail logs), add ``#define DEBUG`` at the top of
``board/spacemit/k1/spl.c`` and rebuild. SPL then passes ``puts`` as
the firmware's log callback; otherwise the callback is ``NULL`` and
the firmware runs silently.
References
~~~~~~~~~~~
- `DDR firmware repository <https://github.com/spacemit-com/spacemit-firmware>`_
- `SpacemiT vendor U-Boot (signing tool) <https://gitee.com/spacemit-buildroot/uboot-2022.10>`_
+3 -2
View File
@@ -277,11 +277,12 @@ source "drivers/clk/mvebu/Kconfig"
source "drivers/clk/owl/Kconfig"
source "drivers/clk/qcom/Kconfig"
source "drivers/clk/renesas/Kconfig"
source "drivers/clk/sophgo/Kconfig"
source "drivers/clk/sunxi/Kconfig"
source "drivers/clk/sifive/Kconfig"
source "drivers/clk/sophgo/Kconfig"
source "drivers/clk/spacemit/Kconfig"
source "drivers/clk/starfive/Kconfig"
source "drivers/clk/stm32/Kconfig"
source "drivers/clk/sunxi/Kconfig"
source "drivers/clk/tegra/Kconfig"
source "drivers/clk/ti/Kconfig"
source "drivers/clk/thead/Kconfig"
+1
View File
@@ -48,6 +48,7 @@ obj-$(CONFIG_CLK_RENESAS) += renesas/
obj-$(CONFIG_$(PHASE_)CLK_SCMI) += clk_scmi.o
obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-$(CONFIG_CLK_SOPHGO) += sophgo/
obj-$(CONFIG_CLK_SPACEMIT) += spacemit/
obj-$(CONFIG_CLK_SUNXI) += sunxi/
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
obj-$(CONFIG_CLK_VERSACLOCK) += clk_versaclock.o
+23
View File
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (c) 2025, Junhui Liu <junhui.liu@pigmoral.tech>
config CLK_SPACEMIT
bool "Clock support for SpacemiT SoCs"
depends on CLK || COMPILE_TEST
select REGMAP
select LIB_RATIONAL
help
This enables support clock driver for Spacemit SoC
family.
if CLK_SPACEMIT
config CLK_SPACEMIT_K1
bool "SpacemiT K1 clock support"
select CLK_CCF
help
This enables support clock driver for Spacemit K1 SoC.
It's based on Common Clock Framework.
endif
+7
View File
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
obj-$(CONFIG_CLK_SPACEMIT) += clk_ddn.o clk_mix.o clk_pll.o
obj-$(CONFIG_CLK_SPACEMIT_K1) += clk-k1.o
File diff suppressed because it is too large Load Diff
+79
View File
@@ -0,0 +1,79 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
* Copyright (c) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
* Copyright (c) 2025-2026 RISCstar Ltd.
*
* Authors: Haylen Chu <heylenay@4d2.org>
*/
#ifndef _CLK_COMMON_H_
#define _CLK_COMMON_H_
#include <linux/clk-provider.h>
struct ccu_common;
typedef int (*ccu_init_fn)(struct ccu_common *common);
struct ccu_common {
struct regmap *regmap;
struct regmap *lock_regmap;
const char *name;
const char * const *parents;
size_t num_parents;
ccu_init_fn init;
union {
/* For DDN and MIX */
struct {
u32 reg_ctrl;
u32 reg_fc;
u32 mask_fc;
};
/* For PLL */
struct {
u32 reg_swcr1;
u32 reg_swcr3;
};
};
struct clk clk;
};
#define CCU_COMMON(_id, _name, _parent, _init, _flags) \
.name = #_name, \
.parents = (const char *[]) { _parent }, \
.num_parents = 1, \
.init = _init, \
.clk = { .flags = _flags, .id = _id, } \
#define CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, _init, _flags) \
.name = #_name, \
.parents = _parents, \
.num_parents = _num_p, \
.init = _init, \
.clk = { .flags = _flags, .id = _id, } \
static inline struct ccu_common *clk_to_ccu_common(struct clk *clk)
{
return container_of(clk, struct ccu_common, clk);
}
#define ccu_read(c, reg) \
({ \
struct ccu_common * const __ccu = (c); \
u32 tmp; \
regmap_read(__ccu->regmap, __ccu->reg_##reg, &tmp); \
tmp; \
})
#define ccu_update(c, reg, mask, val) \
({ \
struct ccu_common * const __ccu = (c); \
regmap_update_bits(__ccu->regmap, __ccu->reg_##reg, \
mask, val); \
})
#endif /* _CLK_COMMON_H_ */
+93
View File
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
* Copyright (c) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
* Authors: Haylen Chu <heylenay@4d2.org>
*
* DDN stands for "Divider Denominator Numerator", it's M/N clock with a
* constant x2 factor. This clock hardware follows the equation below,
*
* numerator Fin
* 2 * ------------- = -------
* denominator Fout
*
* Thus, Fout could be calculated with,
*
* Fin denominator
* Fout = ----- * -------------
* 2 numerator
*/
#include <dm/device.h>
#include <regmap.h>
#include <linux/clk-provider.h>
#include <linux/rational.h>
#include "clk_ddn.h"
#define UBOOT_DM_SPACEMIT_CLK_DDN "spacemit_clk_ddn"
static unsigned long ccu_ddn_calc_rate(unsigned long prate, unsigned long num,
unsigned long den, unsigned int pre_div)
{
return prate * den / pre_div / num;
}
static unsigned long ccu_ddn_calc_best_rate(struct ccu_ddn *ddn,
unsigned long rate, unsigned long prate,
unsigned long *num, unsigned long *den)
{
rational_best_approximation(rate, prate / ddn->pre_div,
ddn->den_mask >> ddn->den_shift,
ddn->num_mask >> ddn->num_shift,
den, num);
return ccu_ddn_calc_rate(prate, *num, *den, ddn->pre_div);
}
static unsigned long ccu_ddn_recalc_rate(struct clk *clk)
{
struct ccu_ddn *ddn = clk_to_ccu_ddn(clk);
unsigned int val, num, den;
val = ccu_read(&ddn->common, ctrl);
num = (val & ddn->num_mask) >> ddn->num_shift;
den = (val & ddn->den_mask) >> ddn->den_shift;
return ccu_ddn_calc_rate(clk_get_parent_rate(clk), num, den, ddn->pre_div);
}
static unsigned long ccu_ddn_set_rate(struct clk *clk, unsigned long rate)
{
struct ccu_ddn *ddn = clk_to_ccu_ddn(clk);
unsigned long num, den;
ccu_ddn_calc_best_rate(ddn, rate, clk_get_parent_rate(clk), &num, &den);
ccu_update(&ddn->common, ctrl,
ddn->num_mask | ddn->den_mask,
(num << ddn->num_shift) | (den << ddn->den_shift));
return 0;
}
static const struct clk_ops spacemit_clk_ddn_ops = {
.get_rate = ccu_ddn_recalc_rate,
.set_rate = ccu_ddn_set_rate,
};
int spacemit_ddn_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_DDN,
common->name, common->parents[0]);
}
U_BOOT_DRIVER(spacemit_clk_ddn) = {
.name = UBOOT_DM_SPACEMIT_CLK_DDN,
.id = UCLASS_CLK,
.ops = &spacemit_clk_ddn_ops,
.flags = DM_FLAG_PRE_RELOC,
};
+53
View File
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
* Copyright (c) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
* Copyright (c) 2025-2026 RISCstar Ltd.
*
* Authors: Haylen Chu <heylenay@4d2.org>
*/
#ifndef _CLK_DDN_H_
#define _CLK_DDN_H_
#include <linux/clk-provider.h>
#include "clk_common.h"
struct ccu_ddn {
struct ccu_common common;
unsigned int num_mask;
unsigned int num_shift;
unsigned int den_mask;
unsigned int den_shift;
unsigned int pre_div;
};
#define CCU_DDN_MASK(_num_shift, _num_width) \
GENMASK((_num_shift) + (_num_width) - 1, _num_shift)
#define CCU_DDN_DEFINE(_id, _var, _name, _parent, _reg_ctrl, _num_mask, \
_num_shift, _den_mask, _den_shift, _pre_div, _flags) \
static struct ccu_ddn _var = { \
.common = { \
.reg_ctrl = _reg_ctrl, \
CCU_COMMON(_id, _name, _parent, spacemit_ddn_init, _flags) \
}, \
.num_mask = _num_mask, \
.num_shift = _num_shift, \
.den_mask = _den_mask, \
.den_shift = _den_shift, \
.pre_div = _pre_div, \
}
static inline struct ccu_ddn *clk_to_ccu_ddn(struct clk *clk)
{
struct ccu_common *common = clk_to_ccu_common(clk);
return container_of(common, struct ccu_ddn, common);
}
int spacemit_ddn_init(struct ccu_common *common);
#endif /* _CLK_DDN_H_ */
+403
View File
@@ -0,0 +1,403 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
* Copyright (c) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
* Authors: Haylen Chu <heylenay@4d2.org>
*
* MIX clock type is the combination of mux, factor or divider, and gate
*/
#include <dm/device.h>
#include <dm/uclass.h>
#include <div64.h>
#include <regmap.h>
#include <linux/clk-provider.h>
#include <linux/kernel.h>
#include "clk_mix.h"
#define UBOOT_DM_SPACEMIT_CLK_GATE "spacemit_clk_gate"
#define UBOOT_DM_SPACEMIT_CLK_FACTOR "spacemit_clk_factor"
#define UBOOT_DM_SPACEMIT_CLK_MUX "spacemit_clk_mux"
#define UBOOT_DM_SPACEMIT_CLK_DIV "spacemit_clk_div"
#define UBOOT_DM_SPACEMIT_CLK_FACTOR_GATE "spacemit_clk_factor_gate"
#define UBOOT_DM_SPACEMIT_CLK_MUX_GATE "spacemit_clk_mux_gate"
#define UBOOT_DM_SPACEMIT_CLK_DIV_GATE "spacemit_clk_div_gate"
#define UBOOT_DM_SPACEMIT_CLK_MUX_DIV "spacemit_clk_mux_div"
#define UBOOT_DM_SPACEMIT_CLK_MUX_DIV_GATE "spacemit_clk_mux_div_gate"
#define MIX_FC_TIMEOUT_US 10000
#define MIX_FC_DELAY_US 5
int ccu_gate_disable(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
ccu_update(&mix->common, ctrl, mix->gate.mask, 0);
return 0;
}
int ccu_gate_enable(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_gate_config *gate = &mix->gate;
ccu_update(&mix->common, ctrl, gate->mask, gate->mask);
return 0;
}
static unsigned long ccu_factor_recalc_rate(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
return clk_get_parent_rate(clk) * mix->factor.mul / mix->factor.div;
}
static unsigned long ccu_div_recalc_rate(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_div_config *div = &mix->div;
unsigned long val;
val = ccu_read(&mix->common, ctrl) >> div->shift;
val &= (1 << div->width) - 1;
return divider_recalc_rate(clk, clk_get_parent_rate(clk), val, NULL, 0, div->width);
}
/*
* Some clocks require a "FC" (frequency change) bit to be set after changing
* their rates or reparenting. This bit will be automatically cleared by
* hardware in MIX_FC_TIMEOUT_US, which indicates the operation is completed.
*/
static int ccu_mix_trigger_fc(struct clk *clk)
{
struct ccu_common *common = clk_to_ccu_common(clk);
unsigned int val;
if (common->reg_fc)
return 0;
ccu_update(common, fc, common->mask_fc, common->mask_fc);
return regmap_read_poll_timeout(common->regmap, common->reg_fc,
val, !(val & common->mask_fc),
MIX_FC_DELAY_US,
MIX_FC_TIMEOUT_US);
}
static unsigned long
ccu_mix_calc_best_rate(struct clk *clk, unsigned long rate,
struct clk **best_parent,
unsigned long *best_parent_rate,
u32 *div_val)
{
struct ccu_common *common = clk_to_ccu_common(clk);
struct ccu_mix *mix = clk_to_ccu_mix(clk);
unsigned int parent_num = common->num_parents;
struct ccu_div_config *div = &mix->div;
u32 div_max = 1 << div->width;
unsigned long best_rate = 0;
for (int i = 0; i < parent_num; i++) {
struct udevice *parent_dev;
unsigned long parent_rate;
struct clk *parent;
if (uclass_get_device_by_name(UCLASS_CLK, common->parents[i],
&parent_dev))
continue;
parent = dev_get_clk_ptr(parent_dev);
if (!parent)
continue;
parent_rate = clk_get_rate(parent);
for (int j = 1; j <= div_max; j++) {
unsigned long tmp = DIV_ROUND_CLOSEST_ULL(parent_rate, j);
if (abs(tmp - rate) < abs(best_rate - rate)) {
best_rate = tmp;
if (div_val)
*div_val = j - 1;
if (best_parent) {
*best_parent = parent;
*best_parent_rate = parent_rate;
}
}
}
}
return best_rate;
}
static unsigned long ccu_mix_set_rate(struct clk *clk, unsigned long rate)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_common *common = &mix->common;
struct ccu_div_config *div = &mix->div;
u32 current_div, target_div, mask;
ccu_mix_calc_best_rate(clk, rate, NULL, NULL, &target_div);
current_div = ccu_read(common, ctrl) >> div->shift;
current_div &= (1 << div->width) - 1;
if (current_div == target_div)
return 0;
mask = GENMASK(div->width + div->shift - 1, div->shift);
ccu_update(common, ctrl, mask, target_div << div->shift);
return ccu_mix_trigger_fc(clk);
}
static u8 ccu_mux_get_parent(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_mux_config *mux = &mix->mux;
u8 parent;
parent = ccu_read(&mix->common, ctrl) >> mux->shift;
parent &= (1 << mux->width) - 1;
return parent;
}
static int ccu_mux_set_parent(struct clk *clk, struct clk *parent)
{
struct ccu_common *common = clk_to_ccu_common(clk);
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_mux_config *mux = &mix->mux;
u32 mask;
int i = 0;
mask = GENMASK(mux->width + mux->shift - 1, mux->shift);
for (i = 0; i < common->num_parents; i++) {
if (!strcmp(parent->dev->name, common->parents[i]))
break;
}
if (i == common->num_parents)
return -EINVAL;
ccu_update(&mix->common, ctrl, mask, i << mux->shift);
return ccu_mix_trigger_fc(clk);
}
int spacemit_gate_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_GATE,
common->name, common->parents[0]);
}
static const struct clk_ops spacemit_clk_gate_ops = {
.disable = ccu_gate_disable,
.enable = ccu_gate_enable,
.get_rate = clk_generic_get_rate,
};
U_BOOT_DRIVER(spacemit_clk_gate) = {
.name = UBOOT_DM_SPACEMIT_CLK_GATE,
.id = UCLASS_CLK,
.ops = &spacemit_clk_gate_ops,
.flags = DM_FLAG_PRE_RELOC,
};
int spacemit_factor_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_FACTOR,
common->name, common->parents[0]);
}
static const struct clk_ops spacemit_clk_factor_ops = {
.get_rate = ccu_factor_recalc_rate,
};
U_BOOT_DRIVER(spacemit_clk_factor) = {
.name = UBOOT_DM_SPACEMIT_CLK_FACTOR,
.id = UCLASS_CLK,
.ops = &spacemit_clk_factor_ops,
.flags = DM_FLAG_PRE_RELOC,
};
int spacemit_mux_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
u8 index;
index = ccu_mux_get_parent(clk);
if (index >= common->num_parents)
index = 0;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_MUX,
common->name, common->parents[index]);
}
static const struct clk_ops spacemit_clk_mux_ops = {
.set_parent = ccu_mux_set_parent,
.get_rate = clk_generic_get_rate,
};
U_BOOT_DRIVER(spacemit_clk_mux) = {
.name = UBOOT_DM_SPACEMIT_CLK_MUX,
.id = UCLASS_CLK,
.ops = &spacemit_clk_mux_ops,
.flags = DM_FLAG_PRE_RELOC,
};
int spacemit_div_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_DIV,
common->name, common->parents[0]);
}
static const struct clk_ops spacemit_clk_div_ops = {
.get_rate = ccu_div_recalc_rate,
.set_rate = ccu_mix_set_rate,
};
U_BOOT_DRIVER(spacemit_clk_div) = {
.name = UBOOT_DM_SPACEMIT_CLK_DIV,
.id = UCLASS_CLK,
.ops = &spacemit_clk_div_ops,
.flags = DM_FLAG_PRE_RELOC,
};
int spacemit_factor_gate_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_FACTOR_GATE,
common->name, common->parents[0]);
}
static const struct clk_ops spacemit_clk_factor_gate_ops = {
.disable = ccu_gate_disable,
.enable = ccu_gate_enable,
.get_rate = ccu_factor_recalc_rate,
};
U_BOOT_DRIVER(spacemit_clk_factor_gate) = {
.name = UBOOT_DM_SPACEMIT_CLK_FACTOR_GATE,
.id = UCLASS_CLK,
.ops = &spacemit_clk_factor_gate_ops,
.flags = DM_FLAG_PRE_RELOC,
};
int spacemit_mux_gate_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
u8 index;
index = ccu_mux_get_parent(clk);
if (index >= common->num_parents)
index = 0;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_MUX_GATE,
common->name, common->parents[index]);
}
static const struct clk_ops spacemit_clk_mux_gate_ops = {
.disable = ccu_gate_disable,
.enable = ccu_gate_enable,
.set_parent = ccu_mux_set_parent,
.get_rate = clk_generic_get_rate,
};
U_BOOT_DRIVER(spacemit_clk_mux_gate) = {
.name = UBOOT_DM_SPACEMIT_CLK_MUX_GATE,
.id = UCLASS_CLK,
.ops = &spacemit_clk_mux_gate_ops,
.flags = DM_FLAG_PRE_RELOC,
};
int spacemit_div_gate_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_DIV_GATE,
common->name, common->parents[0]);
}
static const struct clk_ops spacemit_clk_div_gate_ops = {
.disable = ccu_gate_disable,
.enable = ccu_gate_enable,
.get_rate = ccu_div_recalc_rate,
.set_rate = ccu_mix_set_rate,
};
U_BOOT_DRIVER(spacemit_clk_div_gate) = {
.name = UBOOT_DM_SPACEMIT_CLK_DIV_GATE,
.id = UCLASS_CLK,
.ops = &spacemit_clk_div_gate_ops,
.flags = DM_FLAG_PRE_RELOC,
};
int spacemit_mux_div_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
u8 index;
index = ccu_mux_get_parent(clk);
if (index >= common->num_parents)
index = 0;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_MUX_DIV,
common->name, common->parents[index]);
}
static const struct clk_ops spacemit_clk_mux_div_ops = {
.set_parent = ccu_mux_set_parent,
.get_rate = ccu_div_recalc_rate,
.set_rate = ccu_mix_set_rate,
};
U_BOOT_DRIVER(spacemit_clk_mux_div) = {
.name = UBOOT_DM_SPACEMIT_CLK_MUX_DIV,
.id = UCLASS_CLK,
.ops = &spacemit_clk_mux_div_ops,
.flags = DM_FLAG_PRE_RELOC,
};
int spacemit_mux_div_gate_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
u8 index;
index = ccu_mux_get_parent(clk);
if (index >= common->num_parents)
index = 0;
return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_MUX_DIV_GATE,
common->name, common->parents[index]);
}
static const struct clk_ops spacemit_clk_mux_div_gate_ops = {
.disable = ccu_gate_disable,
.enable = ccu_gate_enable,
.set_parent = ccu_mux_set_parent,
.get_rate = ccu_div_recalc_rate,
.set_rate = ccu_mix_set_rate,
};
U_BOOT_DRIVER(spacemit_clk_mux_div_gate) = {
.name = UBOOT_DM_SPACEMIT_CLK_MUX_DIV_GATE,
.id = UCLASS_CLK,
.ops = &spacemit_clk_mux_div_gate_ops,
.flags = DM_FLAG_PRE_RELOC,
};
+224
View File
@@ -0,0 +1,224 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
* Copyright (c) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
* Copyright (c) 2025-2026 RISCstar Ltd.
*
* Authors: Haylen Chu <heylenay@4d2.org>
*/
#ifndef _CLK_MIX_H_
#define _CLK_MIX_H_
#include <linux/clk-provider.h>
#include "clk_common.h"
/**
* struct ccu_gate_config - Gate configuration
*
* @mask: Mask to enable the gate. Some clocks may have more than one bit
* set in this field.
*/
struct ccu_gate_config {
u32 mask;
};
struct ccu_factor_config {
u32 div;
u32 mul;
};
struct ccu_mux_config {
u8 shift;
u8 width;
};
struct ccu_div_config {
u8 shift;
u8 width;
};
struct ccu_mix {
struct ccu_factor_config factor;
struct ccu_gate_config gate;
struct ccu_div_config div;
struct ccu_mux_config mux;
struct ccu_common common;
};
#define CCU_GATE_INIT(_mask) { .mask = _mask }
#define CCU_FACTOR_INIT(_div, _mul) { .div = _div, .mul = _mul }
#define CCU_MUX_INIT(_shift, _width) { .shift = _shift, .width = _width }
#define CCU_DIV_INIT(_shift, _width) { .shift = _shift, .width = _width }
#define CCU_GATE_DEFINE(_id, _var, _name, _parent, _reg_ctrl, \
_mask_gate, _flags) \
static struct ccu_mix _var = { \
.gate = CCU_GATE_INIT(_mask_gate), \
.common = { \
.reg_ctrl = _reg_ctrl, \
CCU_COMMON(_id, _name, _parent, spacemit_gate_init, \
_flags) \
} \
}
#define CCU_FACTOR_DEFINE(_id, _var, _name, _parent, _div, _mul) \
static struct ccu_mix _var = { \
.factor = CCU_FACTOR_INIT(_div, _mul), \
.common = { \
CCU_COMMON(_id, _name, _parent, spacemit_factor_init, \
0) \
} \
}
#define CCU_MUX_DEFINE(_id, _var, _name, _parents, _num_p, _reg_ctrl, \
_shift, _width, _flags) \
static struct ccu_mix _var = { \
.mux = CCU_MUX_INIT(_shift, _width), \
.common = { \
.reg_ctrl = _reg_ctrl, \
CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
spacemit_mux_init, _flags) \
} \
}
#define CCU_DIV_DEFINE(_id, _var, _name, _parent, _reg_ctrl, _shift, \
_width, _flags) \
static struct ccu_mix _var = { \
.div = CCU_DIV_INIT(_shift, _width), \
.common = { \
.reg_ctrl = _reg_ctrl, \
CCU_COMMON(_id, _name, _parent, spacemit_div_init, \
_flags) \
} \
}
#define CCU_FACTOR_GATE_FLAGS_DEFINE(_id, _var, _name, _parent, \
_reg_ctrl, _mask_gate, _div, _mul, \
_flags) \
static struct ccu_mix _var = { \
.gate = CCU_GATE_INIT(_mask_gate), \
.factor = CCU_FACTOR_INIT(_div, _mul), \
.common = { \
.reg_ctrl = _reg_ctrl, \
CCU_COMMON(_id, _name, _parent, \
spacemit_factor_gate_init, _flags) \
} \
}
#define CCU_FACTOR_GATE_DEFINE(_id, _var, _name, _parent, _reg_ctrl, \
_mask_gate, _div, _mul) \
CCU_FACTOR_GATE_FLAGS_DEFINE(_id, _var, _name, _parent, \
_reg_ctrl, _mask_gate, _div, _mul, \
0)
#define CCU_MUX_GATE_DEFINE(_id, _var, _name, _parents, _num_p, \
_reg_ctrl, _shift, _width, _mask_gate, \
_flags) \
static struct ccu_mix _var = { \
.gate = CCU_GATE_INIT(_mask_gate), \
.mux = CCU_MUX_INIT(_shift, _width), \
.common = { \
.reg_ctrl = _reg_ctrl, \
CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
spacemit_mux_gate_init, _flags) \
} \
}
#define CCU_DIV_GATE_DEFINE(_id, _var, _name, _parent, _reg_ctrl, \
_shift, _width, _mask_gate, _flags) \
static struct ccu_mix _var = { \
.gate = CCU_GATE_INIT(_mask_gate), \
.div = CCU_DIV_INIT(_shift, _width), \
.common = { \
.reg_ctrl = _reg_ctrl, \
CCU_COMMON(_id, _name, _parent, \
spacemit_div_gate_init, _flags) \
} \
}
#define CCU_MUX_DIV_GATE_DEFINE(_id, _var, _name, _parents, _num_p, \
_reg_ctrl, _mshift, _mwidth, _muxshift, \
_muxwidth, _mask_gate, _flags) \
static struct ccu_mix _var = { \
.gate = CCU_GATE_INIT(_mask_gate), \
.div = CCU_DIV_INIT(_mshift, _mwidth), \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
.common = { \
.reg_ctrl = _reg_ctrl, \
CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
spacemit_mux_div_gate_init, _flags) \
}, \
}
#define CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(_id, _var, _name, _parents, \
_num_p, _reg_ctrl, _reg_fc, \
_mshift, _mwidth, _mask_fc, \
_muxshift, _muxwidth, \
_mask_gate, _flags) \
static struct ccu_mix _var = { \
.gate = CCU_GATE_INIT(_mask_gate), \
.div = CCU_DIV_INIT(_mshift, _mwidth), \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
.common = { \
.reg_ctrl = _reg_ctrl, \
.reg_fc = _reg_fc, \
.mask_fc = _mask_fc, \
CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
spacemit_mux_div_gate_init, _flags) \
}, \
}
#define CCU_MUX_DIV_FC_DEFINE(_id, _var, _name, _parents, _num_p, \
_reg_ctrl, _reg_fc, _mshift, _mwidth, \
_mask_fc, _muxshift, _muxwidth, _flags) \
static struct ccu_mix _var = { \
.div = CCU_DIV_INIT(_mshift, _mwidth), \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
.common = { \
.reg_ctrl = _reg_ctrl, \
.reg_fc = _reg_fc, \
.mask_fc = _mask_fc, \
CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
spacemit_mux_div_init, _flags) \
}, \
}
#define CCU_MUX_FC_DEFINE(_id, _var, _name, _parents, _num_p, \
_reg_ctrl, _reg_fc, _mask_fc, _muxshift, \
_muxwidth, _flags) \
static struct ccu_mix _var = { \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
.common = { \
.reg_ctrl = _reg_ctrl, \
.reg_fc = _reg_fc, \
.mask_fc = _mask_fc, \
CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
spacemit_mux_init, \
_flags) \
}, \
}
static inline struct ccu_mix *clk_to_ccu_mix(struct clk *clk)
{
struct ccu_common *common = clk_to_ccu_common(clk);
return container_of(common, struct ccu_mix, common);
}
int ccu_gate_enable(struct clk *clk);
int ccu_gate_disable(struct clk *clk);
int spacemit_gate_init(struct ccu_common *common);
int spacemit_factor_init(struct ccu_common *common);
int spacemit_mux_init(struct ccu_common *common);
int spacemit_div_init(struct ccu_common *common);
int spacemit_factor_gate_init(struct ccu_common *common);
int spacemit_div_gate_init(struct ccu_common *common);
int spacemit_mux_gate_init(struct ccu_common *common);
int spacemit_mux_div_init(struct ccu_common *common);
int spacemit_mux_div_gate_init(struct ccu_common *common);
#endif /* _CLK_MIX_H_ */
+157
View File
@@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
* Copyright (c) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
* Authors: Haylen Chu <heylenay@4d2.org>
*/
#include <dm/device.h>
#include <regmap.h>
#include <linux/bug.h>
#include <linux/clk-provider.h>
#include "clk_pll.h"
#define UBOOT_DM_SPACEMIT_CLK_PLL "spacemit_clk_pll"
#define PLL_TIMEOUT_US 3000
#define PLL_DELAY_US 5
#define PLL_SWCR3_EN ((u32)BIT(31))
#define PLL_SWCR3_MASK GENMASK(30, 0)
static const struct ccu_pll_rate_tbl *ccu_pll_lookup_best_rate(struct ccu_pll *pll,
unsigned long rate)
{
struct ccu_pll_config *config = &pll->config;
const struct ccu_pll_rate_tbl *best_entry;
unsigned long best_delta = ULONG_MAX;
int i;
for (i = 0; i < config->tbl_num; i++) {
const struct ccu_pll_rate_tbl *entry = &config->rate_tbl[i];
unsigned long delta = abs(entry->rate - rate);
if (delta < best_delta) {
best_delta = delta;
best_entry = entry;
}
}
return best_entry;
}
static const struct ccu_pll_rate_tbl *ccu_pll_lookup_matched_entry(struct ccu_pll *pll)
{
struct ccu_pll_config *config = &pll->config;
u32 swcr1, swcr3;
int i;
swcr1 = ccu_read(&pll->common, swcr1);
swcr3 = ccu_read(&pll->common, swcr3);
swcr3 &= PLL_SWCR3_MASK;
for (i = 0; i < config->tbl_num; i++) {
const struct ccu_pll_rate_tbl *entry = &config->rate_tbl[i];
if (swcr1 == entry->swcr1 && swcr3 == entry->swcr3)
return entry;
}
return NULL;
}
static void ccu_pll_update_param(struct ccu_pll *pll, const struct ccu_pll_rate_tbl *entry)
{
struct ccu_common *common = &pll->common;
regmap_write(common->regmap, common->reg_swcr1, entry->swcr1);
ccu_update(common, swcr3, PLL_SWCR3_MASK, entry->swcr3);
}
static int ccu_pll_enable(struct clk *clk)
{
struct ccu_pll *pll = clk_to_ccu_pll(clk);
struct ccu_common *common = &pll->common;
unsigned int tmp;
ccu_update(common, swcr3, PLL_SWCR3_EN, PLL_SWCR3_EN);
/* check lock status */
return regmap_read_poll_timeout(common->lock_regmap,
pll->config.reg_lock,
tmp,
tmp & pll->config.mask_lock,
PLL_DELAY_US, PLL_TIMEOUT_US);
}
static int ccu_pll_disable(struct clk *clk)
{
struct ccu_common *common = clk_to_ccu_common(clk);
ccu_update(common, swcr3, PLL_SWCR3_EN, 0);
return 0;
}
/*
* PLLs must be gated before changing rate, which is ensured by
* flag CLK_SET_RATE_GATE.
*/
static unsigned long ccu_pll_set_rate(struct clk *clk, unsigned long rate)
{
struct ccu_pll *pll = clk_to_ccu_pll(clk);
const struct ccu_pll_rate_tbl *entry;
entry = ccu_pll_lookup_best_rate(pll, rate);
ccu_pll_update_param(pll, entry);
return 0;
}
static unsigned long ccu_pll_recalc_rate(struct clk *clk)
{
struct ccu_pll *pll = clk_to_ccu_pll(clk);
const struct ccu_pll_rate_tbl *entry;
entry = ccu_pll_lookup_matched_entry(pll);
WARN_ON_ONCE(!entry);
return entry ? entry->rate : 0;
}
static const struct clk_ops spacemit_clk_pll_ops = {
.enable = ccu_pll_enable,
.disable = ccu_pll_disable,
.set_rate = ccu_pll_set_rate,
.get_rate = ccu_pll_recalc_rate,
};
int spacemit_pll_init(struct ccu_common *common)
{
struct clk *clk = &common->clk;
struct ccu_pll *pll = clk_to_ccu_pll(clk);
int ret;
ret = clk_register(clk, UBOOT_DM_SPACEMIT_CLK_PLL,
common->name, common->parents[0]);
if (ret)
return ret;
if (ccu_pll_lookup_matched_entry(pll))
return 0;
ccu_pll_disable(clk);
ccu_pll_update_param(pll, &pll->config.rate_tbl[0]);
return 0;
}
U_BOOT_DRIVER(spacemit_clk_pll) = {
.name = UBOOT_DM_SPACEMIT_CLK_PLL,
.id = UCLASS_CLK,
.ops = &spacemit_clk_pll_ops,
.flags = DM_FLAG_PRE_RELOC,
};
+81
View File
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
* Copyright (c) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
* Copyright (c) 2025-2026 RISCstar Ltd.
*
* Authors: Haylen Chu <heylenay@4d2.org>
*/
#ifndef _CLK_PLL_H_
#define _CLK_PLL_H_
#include <linux/clk-provider.h>
#include "clk_common.h"
/**
* struct ccu_pll_rate_tbl - Structure mapping between PLL rate and register
* configuration.
*
* @rate: PLL rate
* @swcr1: Register value of PLLX_SW1_CTRL (PLLx_SWCR1).
* @swcr3: Register value of the PLLx_SW3_CTRL's lowest 31 bits of
* PLLx_SW3_CTRL (PLLx_SWCR3). This highest bit is for enabling
* the PLL and not contained in this field.
*/
struct ccu_pll_rate_tbl {
unsigned long rate;
u32 swcr1;
u32 swcr3;
};
#define CCU_PLL_RATE(_rate, _swcr1, _swcr3) \
{ \
.rate = _rate, \
.swcr1 = _swcr1, \
.swcr3 = _swcr3, \
}
struct ccu_pll_config {
const struct ccu_pll_rate_tbl *rate_tbl;
u32 tbl_num;
u32 reg_lock;
u32 mask_lock;
};
struct ccu_pll {
struct ccu_common common;
struct ccu_pll_config config;
};
#define CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock) \
{ \
.rate_tbl = (_table), \
.tbl_num = sizeof(_table) / sizeof((_table)[0]), \
.reg_lock = (_reg_lock), \
.mask_lock = (_mask_lock), \
}
#define CCU_PLL_DEFINE(_id, _var, _name, _parent, _table, _reg_swcr1, \
_reg_swcr3, _reg_lock, _mask_lock, _flags) \
static struct ccu_pll _var = { \
.config = CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock), \
.common = { \
.reg_swcr1 = _reg_swcr1, \
.reg_swcr3 = _reg_swcr3, \
CCU_COMMON(_id, _name, _parent, spacemit_pll_init, _flags) \
} \
}
static inline struct ccu_pll *clk_to_ccu_pll(struct clk *clk)
{
struct ccu_common *common = clk_to_ccu_common(clk);
return container_of(common, struct ccu_pll, common);
}
int spacemit_pll_init(struct ccu_common *common);
#endif /* _CLK_PLL_H_ */
+8
View File
@@ -449,6 +449,14 @@ config SANDBOX_GPIO_COUNT
of 'anonymous' GPIOs that do not belong to any device or bank.
Select a suitable value depending on your needs.
config SPACEMIT_GPIO
bool "Spacemit K1 GPIO driver"
depends on DM_GPIO && TARGET_SPACEMIT_K1
help
Support the GPIO device in Spacemit SoCs. The GPIOs are arranged
into a number of banks (different for each SoC type) each with 32
GPIOs.
config SUNXI_GPIO
bool "Allwinner GPIO driver"
depends on ARCH_SUNXI
+1
View File
@@ -66,6 +66,7 @@ obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o
obj-$(CONFIG_MSM_GPIO) += msm_gpio.o
obj-$(CONFIG_$(PHASE_)PCF8575_GPIO) += pcf8575_gpio.o
obj-$(CONFIG_$(PHASE_)QCOM_PMIC_GPIO) += qcom_pmic_gpio.o qcom_spmi_gpio.o
obj-$(CONFIG_$(PHASE_)SPACEMIT_GPIO) += spacemit_gpio.o
obj-$(CONFIG_MT7620_GPIO) += mt7620_gpio.o
obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o
obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o
+253
View File
@@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2025-2026 RISCstar Ltd.
*/
#include <asm/gpio.h>
#include <clk.h>
#include <dm/device.h>
#include <dm/device_compat.h>
#include <dm/pinctrl.h>
#include <dm/read.h>
#include <dm/uclass.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <log.h>
#define GPIO_BANK_SIZE 32
#define GPIO_TO_BANK(pin) ((pin) / GPIO_BANK_SIZE)
#define GPIO_TO_BIT(pin) ((pin) % GPIO_BANK_SIZE)
static inline int gpio_to_reg_offset(unsigned int pin)
{
unsigned int bank = GPIO_TO_BANK(pin);
if (bank == 0)
return 0;
else if (bank == 1)
return 4;
else if (bank == 2)
return 8;
else if (bank == 3)
return 0x100;
log_warning("Use default GPIO bank for an invalid GPIO[%d].\n", pin);
return 0;
}
#define REG_PLR(pin) (0x00 + gpio_to_reg_offset(pin))
#define REG_PDR(pin) (0x0c + gpio_to_reg_offset(pin))
#define REG_PSR(pin) (0x18 + gpio_to_reg_offset(pin))
#define REG_PCR(pin) (0x24 + gpio_to_reg_offset(pin))
#define REG_SDR(pin) (0x54 + gpio_to_reg_offset(pin))
#define REG_CDR(pin) (0x60 + gpio_to_reg_offset(pin))
struct spacemit_gpio_data {
u16 gpio_base;
u16 gpio_count;
u8 num_banks;
};
struct spacemit_gpio_priv {
void __iomem *regs;
};
static int spacemit_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
struct ofnode_phandle_args *args)
{
struct spacemit_gpio_data *data;
u32 bank, offset, flags;
data = (struct spacemit_gpio_data *)dev_get_driver_data(dev);
if (args->args_count < 3) {
dev_err(dev, "Invalid args count: %d, expected 3\n",
args->args_count);
return -EINVAL;
}
bank = args->args[0];
offset = args->args[1];
flags = args->args[2];
if (bank >= data->num_banks) {
dev_err(dev, "Invalid gpio bank: %u (max %u)\n",
bank, data->num_banks - 1);
return -EINVAL;
}
if (offset >= GPIO_BANK_SIZE) {
dev_err(dev, "Invalid offset: %u (max 31)\n", offset);
return -EINVAL;
}
desc->offset = bank * GPIO_BANK_SIZE + offset;
desc->flags = gpio_flags_xlate(flags);
return 0;
}
static int spacemit_gpio_get_value(struct udevice *dev, unsigned int offset)
{
struct spacemit_gpio_priv *priv = dev_get_priv(dev);
void __iomem *addr;
u32 value, mask;
addr = priv->regs + REG_PLR(offset);
value = readl(addr);
mask = 1 << GPIO_TO_BIT(offset);
return !!(value & mask);
}
static int spacemit_gpio_get_function(struct udevice *dev, unsigned int offset)
{
struct spacemit_gpio_priv *priv = dev_get_priv(dev);
void __iomem *addr;
u32 value, mask;
addr = priv->regs + REG_PDR(offset);
value = readl(addr);
mask = 1 << GPIO_TO_BIT(offset);
if (value & mask)
return GPIOF_OUTPUT;
return GPIOF_INPUT;
}
static int spacemit_gpio_get_flags(struct udevice *dev, unsigned int offset,
ulong *flagsp)
{
ulong flags = 0;
u32 dir;
dir = spacemit_gpio_get_function(dev, offset);
if (dir) {
flags |= GPIOD_IS_OUT;
if (spacemit_gpio_get_value(dev, offset))
flags |= GPIOD_IS_OUT_ACTIVE;
} else {
flags |= GPIOD_IS_IN;
}
*flagsp = flags;
return 0;
}
static int spacemit_gpio_set_flags(struct udevice *dev, unsigned int offset,
ulong flags)
{
struct spacemit_gpio_priv *priv = dev_get_priv(dev);
void __iomem *addr;
int value;
value = (flags & GPIOD_IS_OUT_ACTIVE) ? 1 : 0;
if (flags & GPIOD_IS_IN) {
addr = priv->regs + REG_CDR(offset);
writel(1 << GPIO_TO_BIT(offset), addr);
}
if (flags & GPIOD_IS_OUT) {
if (value) {
addr = priv->regs + REG_PSR(offset);
writel(1 << GPIO_TO_BIT(offset), addr);
} else {
addr = priv->regs + REG_PCR(offset);
writel(1 << GPIO_TO_BIT(offset), addr);
}
addr = priv->regs + REG_SDR(offset);
writel(1 << GPIO_TO_BIT(offset), addr);
}
return 0;
}
static int spacemit_gpio_request(struct udevice *dev, unsigned int offset,
const char *label)
{
const struct pinctrl_ops *ops;
struct udevice *pctldev;
int ret;
ret = uclass_first_device_err(UCLASS_PINCTRL, &pctldev);
if (ret)
return ret;
ops = pinctrl_get_ops(pctldev);
if (!ops->gpio_request_enable)
return -ENOSYS;
return ops->gpio_request_enable(pctldev, offset);
}
static int spacemit_gpio_rfree(struct udevice *dev, unsigned int offset)
{
const struct pinctrl_ops *ops;
struct udevice *pctldev;
int ret;
ret = uclass_first_device_err(UCLASS_PINCTRL, &pctldev);
if (ret)
return ret;
ops = pinctrl_get_ops(pctldev);
if (!ops->gpio_disable_free)
return -ENOSYS;
return ops->gpio_disable_free(pctldev, offset);
}
static const struct dm_gpio_ops spacemit_gpio_ops = {
.request = spacemit_gpio_request,
.rfree = spacemit_gpio_rfree,
.xlate = spacemit_gpio_xlate,
.get_value = spacemit_gpio_get_value,
.get_function = spacemit_gpio_get_function,
.get_flags = spacemit_gpio_get_flags,
.set_flags = spacemit_gpio_set_flags,
};
static int spacemit_gpio_probe(struct udevice *dev)
{
struct spacemit_gpio_priv *priv;
struct spacemit_gpio_data *data;
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct clk_bulk clks;
int ret;
data = (struct spacemit_gpio_data *)dev_get_driver_data(dev);
priv = dev_get_priv(dev);
priv->regs = dev_read_addr_ptr(dev);
if (!priv->regs) {
dev_err(dev, "Fail to get base address\n");
return -EINVAL;
}
uc_priv->bank_name = "GPIO";
uc_priv->gpio_count = data->gpio_count;
uc_priv->gpio_base = data->gpio_base;
ret = clk_get_bulk(dev, &clks);
if (ret) {
dev_err(dev, "Fail to get bulk clks\n");
return ret;
}
ret = clk_enable_bulk(&clks);
if (ret) {
dev_err(dev, "Fail to enable bulk clks\n");
goto out;
}
return 0;
out:
clk_release_bulk(&clks);
return ret;
}
static const struct spacemit_gpio_data k1_gpio_data = {
.num_banks = 4,
.gpio_count = 128,
.gpio_base = 0,
};
static const struct udevice_id spacemit_gpio_ids[] = {
{ .compatible = "spacemit,k1-gpio", .data = (uintptr_t)&k1_gpio_data, },
{ /* sentinel */ }
};
U_BOOT_DRIVER(k1_gpio) = {
.name = "spacemit_k1_gpio",
.id = UCLASS_GPIO,
.of_match = spacemit_gpio_ids,
.ops = &spacemit_gpio_ops,
.priv_auto = sizeof(struct spacemit_gpio_priv),
.probe = spacemit_gpio_probe,
};
+7
View File
@@ -797,6 +797,13 @@ config SYS_I2C_IHS
help
Support for gdsys IHS I2C driver on FPGA bus.
config SYS_I2C_SPACEMIT_K1
bool "Spacemit K1 I2C driver"
depends on DM_I2C
help
Support for Spacemit I2C controller. It's based on
Driver Model.
source "drivers/i2c/muxes/Kconfig"
endif
+1
View File
@@ -47,6 +47,7 @@ obj-$(CONFIG_SYS_I2C_RZ_RIIC) += rz_riic.o
obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o exynos_hs_i2c.o
obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o
obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o
obj-$(CONFIG_SYS_I2C_SPACEMIT_K1) += k1_i2c.o
obj-$(CONFIG_SYS_I2C_STM32F7) += stm32f7_i2c.o
obj-$(CONFIG_SYS_I2C_SUN6I_P2WI) += sun6i_p2wi.o
obj-$(CONFIG_SYS_I2C_SUN8I_RSB) += sun8i_rsb.o
+523
View File
@@ -0,0 +1,523 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2023-2026 Spacemit, Inc
* Copyright (C) 2025-2026 RISCstar Ltd.
*/
#include <asm/io.h>
#include <clk.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <i2c.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <reset.h>
#include "k1_i2c.h"
#define ICR_OFFSET 0x00
#define ISR_OFFSET 0x04
#define ISAR_OFFSET 0x08
#define IDBR_OFFSET 0x0c
#define ILCR_OFFSET 0x10
#define IWCR_OFFSET 0x14
#define IRCR_OFFSET 0x18
#define IBMR_OFFSET 0x1c
#define WFIFO_OFFSET 0x20
#define WFIFO_WPTR_OFFSET 0x24
#define WFIFO_RPTR_OFFSET 0x28
#define RFIFO_OFFSET 0x2c
#define RFIFO_WPTR_OFFSET 0x30
#define RFIFO_RPTR_OFFSET 0x34
/* All transfers are described by this data structure */
struct k1_i2c_msg {
u8 condition;
u8 acknack;
u8 direction;
u8 data;
};
struct k1_i2c {
u32 icr;
u32 isr;
u32 isar;
u32 idbr;
u32 ilcr;
u32 iwcr;
u32 irst_cyc;
u32 ibmr;
};
struct k1_i2c_priv {
int id;
void __iomem *base;
struct clk clk;
u32 clk_rate;
};
/*
* i2c_reset: - reset the host controller
*
*/
static void i2c_reset(void __iomem *base)
{
u32 icr_mode;
u32 val;
/* Save bus mode (standard or fast speed) for later use */
icr_mode = readl(base + ICR_OFFSET) & ICR_MODE_MASK;
/* disable unit */
val = readl(base + ICR_OFFSET);
writel(val & ~ICR_IUE, base + ICR_OFFSET);
udelay(10);
/* reset the unit */
val = readl(base + ICR_OFFSET);
val |= ICR_UR;
writel(val, base + ICR_OFFSET);
udelay(100);
/* disable unit */
val = readl(base + ICR_OFFSET);
writel(val & ~ICR_IUE, base + ICR_OFFSET);
/* set slave address */
writel(0x00, base + ISR_OFFSET);
/* set control reg values */
writel(I2C_ICR_INIT | icr_mode, base + ICR_OFFSET);
writel(I2C_ISR_INIT, base + ISR_OFFSET); /* set clear interrupt bits */
val = readl(base + ICR_OFFSET);
val |= ICR_IUE;
writel(val, base + ICR_OFFSET); /* enable unit */
udelay(100);
}
static inline bool is_isr_set_or_clr(unsigned long isr, unsigned long set_mask,
unsigned long clr_mask)
{
return ((isr & set_mask) == set_mask) && ((isr & clr_mask) == 0);
}
/*
* i2c_isr_set_cleared: - wait until certain bits of the I2C status register
* are set and cleared
*
* @return: 0 on success or -ETIMEDOUT.
*/
static int i2c_isr_set_cleared(void __iomem *base, unsigned long set_mask,
unsigned long clr_mask)
{
int cnt = 1000, delay = 10, isr, ret;
ret = read_poll_timeout(readl, isr,
is_isr_set_or_clr(isr, set_mask, clr_mask),
delay, delay * cnt, base + ISR_OFFSET);
return ret;
}
/*
* i2c_transfer: - Transfer one byte over the i2c bus
*
* This function can transfer a byte over the i2c bus in both directions.
* It is used by the public API functions.
*
* @return: 0: transfer successful or error code
*/
static int i2c_transfer(void __iomem *base, struct k1_i2c_msg *msg)
{
int ret;
u32 val;
if (!msg)
goto transfer_error_msg_empty;
switch (msg->direction) {
case I2C_WRITE:
/* check if bus is not busy */
if (i2c_isr_set_cleared(base, 0, ISR_IBB))
goto transfer_error_bus_busy;
/* start transmission */
val = readl(base + ICR_OFFSET);
val &= ~ICR_START;
writel(val, base + ICR_OFFSET);
val = readl(base + ICR_OFFSET);
val &= ~ICR_STOP;
writel(val, base + ICR_OFFSET);
writel(msg->data, base + IDBR_OFFSET);
if (msg->condition == I2C_COND_START) {
val = readl(base + ICR_OFFSET);
val |= ICR_START;
writel(val, base + ICR_OFFSET);
}
if (msg->condition == I2C_COND_STOP) {
val = readl(base + ICR_OFFSET);
val |= ICR_STOP;
writel(val, base + ICR_OFFSET);
}
if (msg->acknack == I2C_ACKNAK_SENDNAK) {
val = readl(base + ICR_OFFSET);
val |= ICR_ACKNAK;
writel(val, base + ICR_OFFSET);
}
if (msg->acknack == I2C_ACKNAK_SENDACK) {
val = readl(base + ICR_OFFSET);
val &= ~ICR_ACKNAK;
writel(val, base + ICR_OFFSET);
}
val = readl(base + ICR_OFFSET);
val &= ~ICR_ALDIE;
writel(val, base + ICR_OFFSET);
val = readl(base + ICR_OFFSET);
val |= ICR_TB;
writel(val, base + ICR_OFFSET);
/* transmit register empty? */
if (i2c_isr_set_cleared(base, ISR_ITE, 0))
goto transfer_error_transmit_timeout;
/* clear 'transmit empty' state */
val = readl(base + ISR_OFFSET);
val |= ISR_ITE;
writel(val, base + ISR_OFFSET);
/* wait for ACK from slave */
if (msg->acknack == I2C_ACKNAK_WAITACK)
if (i2c_isr_set_cleared(base, 0, ISR_ACKNAK))
goto transfer_error_ack_missing;
break;
case I2C_READ:
/* check if bus is not busy */
if (i2c_isr_set_cleared(base, 0, ISR_IBB))
goto transfer_error_bus_busy;
/* start receive */
val = readl(base + ICR_OFFSET);
val &= ~ICR_START;
writel(val, base + ICR_OFFSET);
val = readl(base + ICR_OFFSET);
val &= ~ICR_STOP;
writel(val, base + ICR_OFFSET);
if (msg->condition == I2C_COND_START) {
val = readl(base + ICR_OFFSET);
val |= ICR_START;
writel(val, base + ICR_OFFSET);
}
if (msg->condition == I2C_COND_STOP) {
val = readl(base + ICR_OFFSET);
val |= ICR_STOP;
writel(val, base + ICR_OFFSET);
}
if (msg->acknack == I2C_ACKNAK_SENDNAK) {
val = readl(base + ICR_OFFSET);
val |= ICR_ACKNAK;
writel(val, base + ICR_OFFSET);
}
if (msg->acknack == I2C_ACKNAK_SENDACK) {
val = readl(base + ICR_OFFSET);
val &= ~ICR_ACKNAK;
writel(val, base + ICR_OFFSET);
}
val = readl(base + ICR_OFFSET);
val &= ~ICR_ALDIE;
writel(val, base + ICR_OFFSET);
val = readl(base + ICR_OFFSET);
val |= ICR_TB;
writel(val, base + ICR_OFFSET);
/* receive register full? */
if (i2c_isr_set_cleared(base, ISR_IRF, 0))
goto transfer_error_receive_timeout;
msg->data = readl(base + IDBR_OFFSET);
/* clear 'receive empty' state */
val = readl(base + ISR_OFFSET);
val |= ISR_IRF;
writel(val, base + ISR_OFFSET);
break;
default:
goto transfer_error_illegal_param;
}
return 0;
transfer_error_msg_empty:
debug("%s: error: 'msg' is empty\n", __func__);
ret = -EINVAL;
goto i2c_transfer_finish;
transfer_error_transmit_timeout:
debug("%s: error: transmit timeout\n", __func__);
ret = -ETIMEDOUT;
goto i2c_transfer_finish;
transfer_error_ack_missing:
debug("%s: error: ACK missing\n", __func__);
ret = -EREMOTEIO;
goto i2c_transfer_finish;
transfer_error_receive_timeout:
debug("%s: error: receive timeout\n", __func__);
ret = -ETIMEDOUT;
goto i2c_transfer_finish;
transfer_error_illegal_param:
debug("%s: error: illegal parameters\n", __func__);
ret = -EINVAL;
goto i2c_transfer_finish;
transfer_error_bus_busy:
debug("%s: error: bus is busy\n", __func__);
ret = -EIO;
goto i2c_transfer_finish;
i2c_transfer_finish:
debug("%s: ISR: 0x%04x\n", __func__, readl(base + ISR_OFFSET));
i2c_reset(base);
return ret;
}
static int __i2c_read(void __iomem *base, uchar chip, u8 *addr, int alen,
uchar *buffer, int len)
{
struct k1_i2c_msg msg;
int ret;
debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",
__func__, chip, *addr, alen, len);
if (len == 0) {
pr_err("reading zero byte is invalid\n");
return -EINVAL;
}
i2c_reset(base);
/* dummy chip address write */
debug("%s: dummy chip address write\n", __func__);
msg.condition = I2C_COND_START;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = (chip << 1);
msg.data &= 0xFE;
ret = i2c_transfer(base, &msg);
if (ret)
return ret;
/*
* send memory address bytes;
* alen defines how much bytes we have to send.
*/
while (--alen >= 0) {
debug("%s: send address byte %02x (alen=%d)\n",
__func__, *addr, alen);
msg.condition = I2C_COND_NORMAL;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = addr[alen];
ret = i2c_transfer(base, &msg);
if (ret)
return ret;
}
/* start read sequence */
debug("%s: start read sequence\n", __func__);
msg.condition = I2C_COND_START;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = (chip << 1);
msg.data |= 0x01;
ret = i2c_transfer(base, &msg);
if (ret)
return ret;
/* read bytes; send NACK at last byte */
while (len--) {
if (len == 0) {
msg.condition = I2C_COND_STOP;
msg.acknack = I2C_ACKNAK_SENDNAK;
} else {
msg.condition = I2C_COND_NORMAL;
msg.acknack = I2C_ACKNAK_SENDACK;
}
msg.direction = I2C_READ;
msg.data = 0x00;
ret = i2c_transfer(base, &msg);
if (ret)
return ret;
*buffer = msg.data;
debug("%s: reading byte (%p)=0x%02x\n",
__func__, buffer, *buffer);
buffer++;
}
i2c_reset(base);
return 0;
}
static int __i2c_write(struct k1_i2c *base, uchar chip, u8 *addr, int alen,
uchar *buffer, int len)
{
struct k1_i2c_msg msg;
int ret;
debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",
__func__, chip, *addr, alen, len);
i2c_reset(base);
/* chip address write */
debug("%s: chip address write\n", __func__);
msg.condition = I2C_COND_START;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = (chip << 1);
msg.data &= 0xFE;
ret = i2c_transfer(base, &msg);
if (ret)
return ret;
/*
* send memory address bytes;
* alen defines how much bytes we have to send.
*/
while (--alen >= 0) {
debug("%s: send address byte %02x (alen=%d)\n",
__func__, *addr, alen);
msg.condition = I2C_COND_NORMAL;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = addr[alen];
ret = i2c_transfer(base, &msg);
if (ret)
return ret;
}
/* write bytes; send NACK at last byte */
while (len--) {
debug("%s: writing byte (%p)=0x%02x\n",
__func__, buffer, *buffer);
if (len == 0)
msg.condition = I2C_COND_STOP;
else
msg.condition = I2C_COND_NORMAL;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = *(buffer++);
ret = i2c_transfer(base, &msg);
if (ret)
return ret;
}
i2c_reset(base);
return 0;
}
static int k1_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
{
struct k1_i2c_priv *i2c = dev_get_priv(bus);
struct i2c_msg *dmsg, *omsg, dummy;
memset(&dummy, 0, sizeof(struct i2c_msg));
/*
* We expect either two messages (one with an offset and one with the
* actual data) or one message (just data or offset/data combined)
*/
if (nmsgs > 2 || nmsgs == 0) {
debug("%s: Only one or two messages are supported.", __func__);
return -EINVAL;
}
omsg = nmsgs == 1 ? &dummy : msg;
dmsg = nmsgs == 1 ? msg : msg + 1;
if (dmsg->flags & I2C_M_RD)
return __i2c_read(i2c->base, dmsg->addr, omsg->buf,
omsg->len, dmsg->buf, dmsg->len);
else
return __i2c_write(i2c->base, dmsg->addr, omsg->buf,
omsg->len, dmsg->buf, dmsg->len);
}
static int k1_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
{
struct k1_i2c_priv *priv = dev_get_priv(bus);
void __iomem *base = priv->base;
u32 val;
if (speed > I2C_SPEED_STANDARD_RATE)
val = ICR_FM;
else
val = ICR_SM;
clrsetbits_le32(base + ICR_OFFSET, ICR_MODE_MASK, val);
return 0;
}
static int k1_i2c_probe(struct udevice *bus)
{
struct k1_i2c_priv *priv = dev_get_priv(bus);
struct reset_ctl reset;
int ret;
priv->id = dev_seq(bus);
/*
* The upstream K1 dts intentionally omits the 'resets' property
* on i2c nodes — the apbc clock-enable path performs the
* controller reset internally as part of clock gating. Treat the
* reset lookup as optional so we work on the kernel-mainline DT.
*/
ret = reset_get_by_index(bus, 0, &reset);
if (!ret) {
reset_assert(&reset);
udelay(10);
reset_deassert(&reset);
udelay(10);
} else if (ret != -ENOENT && ret != -ENODATA) {
dev_err(bus, "%s: reset lookup failed (%d)\n", __func__, ret);
return ret;
}
ret = clk_get_by_index(bus, 0, &priv->clk);
if (ret)
return ret;
ret = clk_enable(&priv->clk);
if (ret && ret != -ENOSYS && ret != -EOPNOTSUPP) {
debug("%s: failed to enable clock\n", __func__);
return ret;
}
priv->clk_rate = clk_get_rate(&priv->clk);
priv->base = (void *)devfdt_get_addr_ptr(bus);
k1_i2c_set_bus_speed(bus, priv->clk_rate);
return 0;
}
static const struct dm_i2c_ops k1_i2c_ops = {
.xfer = k1_i2c_xfer,
.set_bus_speed = k1_i2c_set_bus_speed,
};
static const struct udevice_id k1_i2c_ids[] = {
{ .compatible = "spacemit,k1-i2c" },
{ }
};
U_BOOT_DRIVER(i2c_spacemit) = {
.name = "i2c_spacemit",
.id = UCLASS_I2C,
.of_match = k1_i2c_ids,
.probe = k1_i2c_probe,
.priv_auto = sizeof(struct k1_i2c_priv),
.ops = &k1_i2c_ops,
};
+69
View File
@@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2023-2026 Spacemit Ltd.
* Copyright (C) 2025-2026 RISCstar Ltd.
*/
#ifndef __SPACEMIT_I2C_H
#define __SPACEMIT_I2C_H
/* Shall the current transfer have a start/stop condition? */
#define I2C_COND_NORMAL 0
#define I2C_COND_START 1
#define I2C_COND_STOP 2
/* Shall the current transfer be ack/nacked or being waited for it? */
#define I2C_ACKNAK_WAITACK 1
#define I2C_ACKNAK_SENDACK 2
#define I2C_ACKNAK_SENDNAK 4
/* Specify who shall transfer the data (master or slave) */
#define I2C_READ 0
#define I2C_WRITE 1
#if (CONFIG_SYS_I2C_SPEED == 400000)
#define I2C_ICR_INIT (ICR_FM | ICR_BEIE | ICR_IRFIE | ICR_ITEIE | \
ICR_GCD | ICR_SCLE)
#else
#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | \
ICR_SCLE)
#endif
/* ----- Control register bits ---------------------------------------- */
#define ICR_START 0x1 /* start bit */
#define ICR_STOP 0x2 /* stop bit */
#define ICR_ACKNAK 0x4 /* send ACK(0) or NAK(1) */
#define ICR_TB 0x8 /* transfer byte bit */
#define ICR_MA BIT(12) /* master abort */
#define ICR_SCLE BIT(13) /* master clock enable, mona SCLEA */
#define ICR_IUE BIT(14) /* unit enable */
#define ICR_GCD BIT(21) /* general call disable */
#define ICR_ITEIE BIT(19) /* enable tx interrupts */
#define ICR_IRFIE BIT(20) /* enable rx interrupts, mona: DRFIE */
#define ICR_BEIE BIT(22) /* enable bus error ints */
#define ICR_SSDIE BIT(24) /* slave STOP detected int enable */
#define ICR_ALDIE BIT(18) /* enable arbitration interrupt */
#define ICR_SADIE BIT(23) /* slave address detected int enable */
#define ICR_UR BIT(10) /* unit reset */
#define ICR_SM (0x0) /* Standard Mode */
#define ICR_FM BIT(8) /* Fast Mode */
#define ICR_MODE_MASK (0x300) /* Mode mask */
/* ----- Status register bits ----------------------------------------- */
#define ISR_RWM BIT(13) /* read/write mode */
#define ISR_ACKNAK BIT(14) /* ack/nak status */
#define ISR_UB BIT(15) /* unit busy */
#define ISR_IBB BIT(16) /* bus busy */
#define ISR_SSD BIT(24) /* slave stop detected */
#define ISR_ALD BIT(18) /* arbitration loss detected */
#define ISR_ITE BIT(19) /* tx buffer empty */
#define ISR_IRF BIT(20) /* rx buffer full */
#define ISR_GCAD BIT(21) /* general call address detected */
#define ISR_SAD BIT(23) /* slave address detected */
#define ISR_BED BIT(22) /* bus error no ACK/NAK */
#define I2C_ISR_INIT 0x1FDE000
#endif
+2 -3
View File
@@ -8,14 +8,13 @@ spi-nor-y := sf_probe.o spi-nor-ids.o
ifdef CONFIG_XPL_BUILD
obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o
endif
ifeq ($(CONFIG_$(PHASE_)SPI_FLASH_TINY),y)
spi-nor-y += spi-nor-tiny.o
else
spi-nor-y += spi-nor-core.o
endif
else
spi-nor-y += spi-nor-core.o
endif
obj-$(CONFIG_SPI_FLASH) += spi-nor.o
obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o
+5 -3
View File
@@ -229,9 +229,11 @@ static int spi_flash_std_remove(struct udevice *dev)
spi_mem_dirmap_destroy(flash->dirmap.rdesc);
}
ret = spi_nor_remove(flash);
if (ret)
return ret;
if (CONFIG_IS_ENABLED(SPI_FLASH_SOFT_RESET)) {
ret = spi_nor_remove(flash);
if (ret)
return ret;
}
if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
spi_flash_mtd_unregister(flash);
+1
View File
@@ -440,6 +440,7 @@ source "drivers/pinctrl/nxp/Kconfig"
source "drivers/pinctrl/qcom/Kconfig"
source "drivers/pinctrl/renesas/Kconfig"
source "drivers/pinctrl/rockchip/Kconfig"
source "drivers/pinctrl/spacemit/Kconfig"
source "drivers/pinctrl/sunxi/Kconfig"
source "drivers/pinctrl/tegra/Kconfig"
source "drivers/pinctrl/uniphier/Kconfig"
+1
View File
@@ -32,6 +32,7 @@ obj-$(CONFIG_$(PHASE_)PINCTRL_ROCKCHIP) += rockchip/
obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
obj-$(CONFIG_PINCTRL_SCMI) += pinctrl-scmi.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_$(PHASE_)PINCTRL_SPACEMIT_K1) += spacemit/
obj-$(CONFIG_PINCTRL_STARFIVE) += starfive/
obj-$(CONFIG_PINCTRL_STI) += pinctrl-sti.o
obj-$(CONFIG_PINCTRL_STM32) += pinctrl_stm32.o
+9
View File
@@ -0,0 +1,9 @@
config PINCTRL_SPACEMIT_K1
bool "Spacemit K1 SoC pinctrl driver"
depends on PINCTRL_GENERIC && DM
help
Supports pin multiplexing control on Spacemit K1 SoCs.
The driver is controlled by a device tree node which contains both
the GPIO definitions and pin control functions for each available
multiplex function.
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += pinctrl-k1.o
+550
View File
@@ -0,0 +1,550 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2024 Yixun Lan <dlan@gentoo.org>
* Copyright (c) 2025-2026 RISCstar Ltd.
*/
#include <clk.h>
#include <dm/device.h>
#include <dm/device_compat.h>
#include <dm/pinctrl.h>
#include <dm/read.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/io.h>
/*
* +---------+----------+-----------+--------+--------+----------+--------+
* | pull | drive | schmitter | slew | edge | strong | mux |
* | up/down | strength | trigger | rate | detect | pull | mode |
* +---------+----------+-----------+--------+--------+----------+--------+
* 3 bits 3 bits 2 bits 1 bit 3 bits 1 bit 3 bits
*/
#define PAD_MUX GENMASK(2, 0)
#define PAD_STRONG_PULL BIT(3)
#define PAD_EDGE_RISE BIT(4)
#define PAD_EDGE_FALL BIT(5)
#define PAD_EDGE_CLEAR BIT(6)
#define PAD_SLEW_RATE GENMASK(12, 11)
#define PAD_SLEW_RATE_EN BIT(7)
#define PAD_SCHMITT GENMASK(9, 8)
#define PAD_DRIVE GENMASK(12, 10)
#define PAD_PULLDOWN BIT(13)
#define PAD_PULLUP BIT(14)
#define PAD_PULL_EN BIT(15)
#define PIN_POWER_STATE_1V8 1800
#define PIN_POWER_STATE_3V3 3300
enum spacemit_pin_io_type {
IO_TYPE_NONE = 0,
IO_TYPE_1V8,
IO_TYPE_3V3,
IO_TYPE_EXTERNAL,
};
struct spacemit_pin_io {
unsigned int pin : 12; // 0~4095
unsigned int io_type : 4; // 0~15
unsigned int ds : 8; // 0~255
unsigned int reserved : 8;
};
struct spacemit_pinctrl_data {
struct spacemit_pin_io *io_pins;
int nr_io_pins;
void __iomem * (*pin_to_reg)(struct udevice *dev, unsigned int pin);
int (*get_gpio_mux)(struct udevice *dev, unsigned int pin);
int (*get_pins)(struct udevice *dev);
int (*get_functions)(struct udevice *dev);
int (*get_io_type)(struct udevice *dev, unsigned int pin);
};
struct spacemit_pinctrl_priv {
void __iomem *regs;
struct spacemit_pin_io *io_pins;
int nr_io_pins;
};
struct spacemit_pin_mux_config {
const struct spacemit_pin *pin;
u32 config;
};
struct spacemit_pin_drv_strength {
unsigned int val : 8;
unsigned int ma : 16;
unsigned int reserved : 8;
};
static char pin_name[PINNAME_SIZE];
/* External: IO voltage via external source, can be 1.8V or 3.3V */
static struct spacemit_pin_io k1_io_pins[] = {
{ 47, IO_TYPE_EXTERNAL, 0, },
{ 48, IO_TYPE_EXTERNAL, 0, },
{ 49, IO_TYPE_EXTERNAL, 0, },
{ 50, IO_TYPE_EXTERNAL, 0, },
{ 51, IO_TYPE_EXTERNAL, 0, },
{ 52, IO_TYPE_EXTERNAL, 0, },
{ 75, IO_TYPE_EXTERNAL, 0, },
{ 76, IO_TYPE_EXTERNAL, 0, },
{ 77, IO_TYPE_EXTERNAL, 0, },
{ 78, IO_TYPE_EXTERNAL, 0, },
{ 79, IO_TYPE_EXTERNAL, 0, },
{ 80, IO_TYPE_EXTERNAL, 0, },
{ 98, IO_TYPE_EXTERNAL, 0, },
{ 99, IO_TYPE_EXTERNAL, 0, },
{ 100, IO_TYPE_EXTERNAL, 0, },
{ 101, IO_TYPE_EXTERNAL, 0, },
{ 102, IO_TYPE_EXTERNAL, 0, },
{ 103, IO_TYPE_EXTERNAL, 0, },
{ 104, IO_TYPE_EXTERNAL, 0, },
{ 105, IO_TYPE_EXTERNAL, 0, },
{ 106, IO_TYPE_EXTERNAL, 0, },
{ 107, IO_TYPE_EXTERNAL, 0, },
{ 108, IO_TYPE_EXTERNAL, 0, },
{ 109, IO_TYPE_EXTERNAL, 0, },
};
static inline int k1_get_pins(struct udevice *dev)
{
return 128;
}
static inline int k1_get_functions(struct udevice *dev)
{
return 7;
}
// The pin number equals to the gpio number.
static void __iomem *k1_pin_to_reg(struct udevice *dev, unsigned int pin)
{
struct spacemit_pinctrl_priv *priv = dev_get_priv(dev);
unsigned int offset = 1;
if (pin < 86) {
offset += pin;
} else if (pin < 93) {
offset += pin + 36;
} else if (pin < 98) {
offset += pin + 23;
} else if (pin == 98) { // QSPI_DAT3
offset += 92;
} else if (pin == 99) { // QSPI_DAT2
offset += 91;
} else if (pin == 100) { // QSPI_DAT1
offset += 90;
} else if (pin == 101) { // QSPI_DAT0
offset += 89;
} else if (pin == 102) { // QSPI_CLK
offset += 94;
} else if (pin == 103) { // QSPI_CS1
offset += 93;
} else if (pin < 111) {
offset += pin + 5;
} else if (pin < 128) {
offset += pin + 19;
} else {
dev_err(dev, "Invalid pin (%u)\n", pin);
return NULL;
}
return priv->regs + (offset << 2);
}
static int k1_get_gpio_mux(struct udevice *dev, unsigned int selector)
{
u32 mux = 0;
if (selector < 70) {
mux = 0;
} else if (selector < 74) {
mux = 1;
} else if (selector < 93) {
mux = 0;
} else if (selector < 104) {
mux = 1;
} else if (selector < 110) {
mux = 4;
} else if (selector < 128) {
mux = 0;
} else {
dev_err(dev, "Invalid pin (%u)\n", selector);
return -EINVAL;
}
return mux;
}
static int k1_get_io_type(struct udevice *dev, unsigned int selector)
{
if (selector < 47)
return IO_TYPE_1V8;
else if (selector < 53)
return IO_TYPE_EXTERNAL;
else if (selector < 75)
return IO_TYPE_1V8;
else if (selector < 81)
return IO_TYPE_EXTERNAL;
else if (selector < 98)
return IO_TYPE_1V8;
else if (selector < 110)
return IO_TYPE_EXTERNAL;
else if (selector < 128)
return IO_TYPE_1V8;
return -EINVAL;
}
/* use IO high level output current as the table */
static struct spacemit_pin_drv_strength spacemit_ds_1v8_tbl[4] = {
{ 0, 11 },
{ 2, 21 },
{ 4, 32 },
{ 6, 42 },
};
static struct spacemit_pin_drv_strength spacemit_ds_3v3_tbl[8] = {
{ 0, 7 },
{ 2, 10 },
{ 4, 13 },
{ 6, 16 },
{ 1, 19 },
{ 3, 23 },
{ 5, 26 },
{ 7, 29 },
};
static inline u8 spacemit_get_ds_value(struct spacemit_pin_drv_strength *tbl,
u32 num, u32 ma)
{
int i;
for (i = 0; i < num; i++)
if (ma <= tbl[i].ma)
return tbl[i].val;
return tbl[num - 1].val;
}
static inline u32 spacemit_get_ds_ma(struct spacemit_pin_drv_strength *tbl,
u32 num, u32 val)
{
int i;
for (i = 0; i < num; i++)
if (val == tbl[i].val)
return tbl[i].ma;
return 0;
}
static inline u8 spacemit_get_drive_strength(enum spacemit_pin_io_type type,
u32 ma)
{
switch (type) {
case IO_TYPE_1V8:
return spacemit_get_ds_value(spacemit_ds_1v8_tbl,
ARRAY_SIZE(spacemit_ds_1v8_tbl),
ma);
case IO_TYPE_3V3:
return spacemit_get_ds_value(spacemit_ds_3v3_tbl,
ARRAY_SIZE(spacemit_ds_3v3_tbl),
ma);
default:
return 0;
}
}
static inline u32 spacemit_get_drive_strength_ma(enum spacemit_pin_io_type type,
u32 value)
{
switch (type) {
case IO_TYPE_1V8:
return spacemit_get_ds_ma(spacemit_ds_1v8_tbl,
ARRAY_SIZE(spacemit_ds_1v8_tbl),
value & 0x6);
case IO_TYPE_3V3:
return spacemit_get_ds_ma(spacemit_ds_3v3_tbl,
ARRAY_SIZE(spacemit_ds_3v3_tbl),
value);
default:
return 0;
}
}
static inline u16 spacemit_dt_get_pin(u32 value)
{
return value >> 16;
}
static inline u16 spacemit_dt_get_pin_mux(u32 value)
{
return value & GENMASK(15, 0);
}
static int spacemit_get_pins_count(struct udevice *dev)
{
struct spacemit_pinctrl_data *data;
data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
if (!data || !data->get_pins)
return -EINVAL;
return data->get_pins(dev);
}
static const char *spacemit_get_pin_name(struct udevice *dev,
unsigned int selector)
{
struct spacemit_pinctrl_data *data;
unsigned int npins;
data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
if (!data || !data->get_pins)
return NULL;
npins = data->get_pins(dev);
if (selector >= npins)
snprintf(pin_name, PINNAME_SIZE, "Error");
else
snprintf(pin_name, PINNAME_SIZE, "PIN%u", selector);
return pin_name;
}
static int spacemit_get_functions_count(struct udevice *dev)
{
struct spacemit_pinctrl_data *data;
data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
if (!data || !data->get_functions)
return -EINVAL;
return data->get_functions(dev);
}
static int spacemit_get_pin_muxing(struct udevice *dev, unsigned int pin,
char *buf, int size)
{
struct spacemit_pinctrl_data *data;
void __iomem *addr;
u32 mux, val;
data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
if (!data || !data->pin_to_reg)
return -EINVAL;
addr = data->pin_to_reg(dev, pin);
if (!addr)
return -EINVAL;
val = readl(addr);
mux = val & PAD_MUX;
snprintf(buf, size, "[%p] 0x%08x MUX%d", addr, val, mux);
return 0;
}
static int spacemit_pinctrl_request_gpio(struct udevice *dev,
unsigned int selector)
{
struct spacemit_pinctrl_data *data;
void __iomem *addr;
int mux;
data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
if (!data || !data->pin_to_reg || !data->get_gpio_mux)
return -EINVAL;
addr = data->pin_to_reg(dev, selector);
mux = data->get_gpio_mux(dev, selector);
if (mux < 0) {
dev_err(dev, "Invalid pin (%d)\n", selector);
return -EINVAL;
}
clrsetbits_le32(addr, PAD_MUX, mux & PAD_MUX);
return 0;
}
static int spacemit_pinctrl_free_gpio(struct udevice *dev,
unsigned int selector)
{
return 0;
}
static int spacemit_pinmux_set(struct udevice *dev, unsigned int pin,
unsigned int mux)
{
struct spacemit_pinctrl_data *data;
void __iomem *addr;
data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
if (!data || !data->pin_to_reg)
return -EINVAL;
addr = data->pin_to_reg(dev, pin);
clrsetbits_le32(addr, PAD_MUX, mux & PAD_MUX);
return 0;
}
static int spacemit_pinmux_property_set(struct udevice *dev, u32 pinmux_group)
{
u32 pin, mux;
pin = spacemit_dt_get_pin(pinmux_group);
mux = spacemit_dt_get_pin_mux(pinmux_group);
return spacemit_pinmux_set(dev, pin, mux);
}
static const struct pinconf_param spacemit_pinconf_params[] = {
{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, U32_MAX },
{ "power-source", PIN_CONFIG_POWER_SOURCE, U32_MAX },
};
static int spacemit_pinconf_set(struct udevice *dev, unsigned int pin_selector,
unsigned int param, unsigned int argument)
{
struct spacemit_pinctrl_data *data;
struct spacemit_pinctrl_priv *priv = dev_get_priv(dev);
void __iomem *addr;
u32 mask = 0;
unsigned int io_type;
u8 ds;
bool found;
int i;
data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
if (!data || !data->pin_to_reg)
return -EINVAL;
addr = data->pin_to_reg(dev, pin_selector);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
clrbits_le32(addr, PAD_PULLUP | PAD_PULLDOWN | PAD_PULL_EN);
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
mask = PAD_PULLDOWN | PAD_PULLUP | PAD_PULL_EN;
clrsetbits_le32(addr, mask, PAD_PULLDOWN | PAD_PULL_EN);
break;
case PIN_CONFIG_BIAS_PULL_UP:
mask = PAD_PULLDOWN | PAD_PULLUP | PAD_PULL_EN;
clrsetbits_le32(addr, mask, PAD_PULLUP | PAD_PULL_EN);
break;
case PIN_CONFIG_DRIVE_STRENGTH:
io_type = IO_TYPE_1V8;
for (i = 0; i < priv->nr_io_pins; i++) {
if (priv->io_pins[i].pin != pin_selector)
continue;
io_type = priv->io_pins[i].io_type;
break;
}
if (io_type != IO_TYPE_3V3 && io_type != IO_TYPE_1V8) {
dev_err(dev, "Invalid IO type (%d)\n", io_type);
return -EINVAL;
}
ds = spacemit_get_drive_strength(io_type, argument);
clrsetbits_le32(addr, PAD_DRIVE, ds);
break;
case PIN_CONFIG_POWER_SOURCE:
for (i = 0, found = false; i < priv->nr_io_pins; i++) {
if (priv->io_pins[i].pin != pin_selector)
continue;
if (argument == PIN_POWER_STATE_3V3) {
priv->io_pins[i].io_type = IO_TYPE_3V3;
found = true;
} else if (argument == PIN_POWER_STATE_1V8) {
priv->io_pins[i].io_type = IO_TYPE_1V8;
found = true;
}
break;
}
if (!found && argument != PIN_POWER_STATE_1V8) {
dev_err(dev, "Invalid power source (%d)\n", argument);
return -EINVAL;
}
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int spacemit_pinctrl_probe(struct udevice *dev)
{
struct spacemit_pinctrl_data *data;
struct spacemit_pinctrl_priv *priv;
struct clk_bulk clks;
size_t size;
int ret;
data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
priv = dev_get_priv(dev);
priv->regs = dev_read_addr_ptr(dev);
if (!priv->regs) {
dev_err(dev, "Fail to get base address\n");
return -EINVAL;
}
priv->nr_io_pins = data->nr_io_pins;
size = priv->nr_io_pins * sizeof(struct spacemit_pin_io);
priv->io_pins = (struct spacemit_pin_io *)memdup(data->io_pins, size);
if (!priv->io_pins) {
dev_err(dev, "Fail to allocate memory\n");
return -ENOMEM;
}
ret = clk_get_bulk(dev, &clks);
if (ret) {
dev_err(dev, "Fail to get bulk clks\n");
goto out_get_clk;
}
ret = clk_enable_bulk(&clks);
if (ret) {
dev_err(dev, "Fail to enable bulk clks\n");
goto out_clks;
}
return 0;
out_clks:
clk_release_bulk(&clks);
out_get_clk:
free(priv->io_pins);
return ret;
}
static const struct spacemit_pinctrl_data k1_pinctrl_data = {
.io_pins = k1_io_pins,
.nr_io_pins = ARRAY_SIZE(k1_io_pins),
.pin_to_reg = k1_pin_to_reg,
.get_gpio_mux = k1_get_gpio_mux,
.get_pins = k1_get_pins,
.get_functions = k1_get_functions,
.get_io_type = k1_get_io_type,
};
static const struct udevice_id spacemit_pinctrl_ids[] = {
{
.compatible = "spacemit,k1-pinctrl",
.data = (uintptr_t)&k1_pinctrl_data,
}, { /* sentinel */ }
};
static const struct pinctrl_ops spacemit_pinctrl_ops = {
.get_pins_count = spacemit_get_pins_count,
.get_pin_name = spacemit_get_pin_name,
.get_functions_count = spacemit_get_functions_count,
.get_pin_muxing = spacemit_get_pin_muxing,
.set_state = pinctrl_generic_set_state,
.gpio_request_enable = spacemit_pinctrl_request_gpio,
.gpio_disable_free = spacemit_pinctrl_free_gpio,
.pinmux_set = spacemit_pinmux_set,
.pinmux_property_set = spacemit_pinmux_property_set,
.pinconf_num_params = ARRAY_SIZE(spacemit_pinconf_params),
.pinconf_params = spacemit_pinconf_params,
.pinconf_set = spacemit_pinconf_set,
};
U_BOOT_DRIVER(spacemit_pinctrl) = {
.name = "spacemit_pinctrl",
.id = UCLASS_PINCTRL,
.of_match = spacemit_pinctrl_ids,
.ops = &spacemit_pinctrl_ops,
.priv_auto = sizeof(struct spacemit_pinctrl_priv),
.probe = spacemit_pinctrl_probe,
};
+8
View File
@@ -434,6 +434,14 @@ config PMIC_RAA215300
allows register access and will bind the sysreset driver
(CONFIG_SYSRESET_RAA215300) if it is enabled.
config PMIC_SPACEMIT_P1
bool "Enable driver for Spacemit P1 power management chip"
depends on DM_PMIC
help
The P1 PMIC integrates multiple functions including
voltage regulators, a watchdog timer, GPIO interfaces, and a
real-time clock.
config DM_PMIC_MTK_PWRAP
bool "Enable driver for MediaTek PMIC Wrapper Support"
help
+1
View File
@@ -32,6 +32,7 @@ obj-$(CONFIG_$(PHASE_)DM_PMIC_TPS80031) += tps80031.o
obj-$(CONFIG_$(PHASE_)PMIC_PALMAS) += palmas.o
obj-$(CONFIG_$(PHASE_)PMIC_LP873X) += lp873x.o
obj-$(CONFIG_$(PHASE_)PMIC_LP87565) += lp87565.o
obj-$(CONFIG_PMIC_SPACEMIT_P1) += pmic_spacemit_p1.o
obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o
obj-$(CONFIG_PMIC_TPS65217) += pmic_tps65217.o
obj-$(CONFIG_PMIC_TPS65219) += tps65219.o
+94
View File
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2025-2026 RISCstar Ltd.
*/
#include <dm.h>
#include <power/pmic.h>
#include <power/spacemit_p1.h>
static int pmic_p1_reg_count(struct udevice *dev)
{
return P1_MAX_REGS;
}
static int pmic_p1_write(struct udevice *dev, uint reg, const u8 *buffer,
int len)
{
int ret;
ret = dm_i2c_write(dev, reg, buffer, len);
if (ret)
pr_err("%s write error on register %02x\n", dev->name, reg);
return ret;
}
static int pmic_p1_read(struct udevice *dev, uint reg, u8 *buffer,
int len)
{
int ret;
ret = dm_i2c_read(dev, reg, buffer, len);
if (ret)
pr_err("%s read error on register %02x\n", dev->name, reg);
return ret;
}
static const struct pmic_child_info p1_children_info[] = {
{ .prefix = "buck", .driver = P1_BUCK_DRIVER },
{ .prefix = "aldo", .driver = P1_ALDO_DRIVER },
{ .prefix = "dldo", .driver = P1_DLDO_DRIVER },
{ },
};
static int pmic_p1_bind(struct udevice *dev)
{
const struct pmic_child_info *p1_children_info =
(struct pmic_child_info *)dev_get_driver_data(dev);
ofnode regulators_node;
int children;
regulators_node = dev_read_subnode(dev, "regulators");
if (!ofnode_valid(regulators_node)) {
debug("%s regulators subnode not found\n", dev->name);
return -EINVAL;
}
children = pmic_bind_children(dev, regulators_node,
p1_children_info);
if (!children)
debug("%s has no children (regulators)\n", dev->name);
return 0;
}
static int pmic_p1_probe(struct udevice *dev)
{
return 0;
}
static struct dm_pmic_ops pmic_p1_ops = {
.reg_count = pmic_p1_reg_count,
.read = pmic_p1_read,
.write = pmic_p1_write,
};
static const struct udevice_id pmic_p1_match[] = {
{
.compatible = "spacemit,p1",
.data = (ulong)&p1_children_info,
}, {
/* sentinel */
}
};
U_BOOT_DRIVER(pmic_p1) = {
.name = "pmic_p1",
.id = UCLASS_PMIC,
.of_match = pmic_p1_match,
.bind = pmic_p1_bind,
.probe = pmic_p1_probe,
.ops = &pmic_p1_ops,
};
+7
View File
@@ -600,3 +600,10 @@ config SANDBOX_PMBUS
(DM_REGULATOR_PMBUS_GENERIC) and the pmbus CLI command can be
exercised by the dm unit tests with no real hardware. Only
useful for testing; say N on real boards.
config DM_REGULATOR_SPACEMIT_P1
bool "Enable driver for Spacemit P1 PMIC regulators"
depends on DM_REGULATOR && PMIC_SPACEMIT_P1
help
Enable implementation of driver-model regulator uclass features
for regulator P1. The driver supports BUCKs, LDOs and SWITCHes.
+1
View File
@@ -29,6 +29,7 @@ obj-$(CONFIG_$(PHASE_)REGULATOR_RK8XX) += rk8xx.o
obj-$(CONFIG_DM_REGULATOR_S2MPS11) += s2mps11_regulator.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o
obj-$(CONFIG_DM_REGULATOR_SPACEMIT_P1) += spacemit_p1_regulator.o
obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PALMAS) += palmas_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PBIAS) += pbias_regulator.o
@@ -0,0 +1,464 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2025-2026 RISCstar Ltd.
*/
#include <dm.h>
#include <dm/lists.h>
#include <errno.h>
#include <log.h>
#include <power/pmic.h>
#include <power/regulator.h>
#include <power/spacemit_p1.h>
struct p1_reg_info {
uint min_uv;
uint step_uv;
u8 vsel_reg;
u8 vsel_sleep_reg;
u8 config_reg;
u8 vsel_mask;
u8 min_sel;
u8 max_sel;
};
static const struct p1_reg_info p1_bucks[] = {
/* BUCK 1 */
{ 500000, 5000, P1_BUCK_VSEL(1), P1_BUCK_SVSEL(1), P1_BUCK_CTRL(1),
BUCK_VSEL_MASK, 0x00, 0xaa },
{ 1375000, 25000, P1_BUCK_VSEL(1), P1_BUCK_SVSEL(1), P1_BUCK_CTRL(1),
BUCK_VSEL_MASK, 0xab, 0xfe },
/* BUCK 2 */
{ 500000, 5000, P1_BUCK_VSEL(2), P1_BUCK_SVSEL(2), P1_BUCK_CTRL(2),
BUCK_VSEL_MASK, 0x00, 0xaa },
{ 1375000, 25000, P1_BUCK_VSEL(2), P1_BUCK_SVSEL(2), P1_BUCK_CTRL(2),
BUCK_VSEL_MASK, 0xab, 0xfe },
/* BUCK 3 */
{ 500000, 5000, P1_BUCK_VSEL(3), P1_BUCK_SVSEL(3), P1_BUCK_CTRL(3),
BUCK_VSEL_MASK, 0x00, 0xaa },
{ 1375000, 25000, P1_BUCK_VSEL(3), P1_BUCK_SVSEL(3), P1_BUCK_CTRL(3),
BUCK_VSEL_MASK, 0xab, 0xfe },
/* BUCK 4 */
{ 500000, 5000, P1_BUCK_VSEL(4), P1_BUCK_SVSEL(4), P1_BUCK_CTRL(4),
BUCK_VSEL_MASK, 0x00, 0xaa },
{ 1375000, 25000, P1_BUCK_VSEL(4), P1_BUCK_SVSEL(4), P1_BUCK_CTRL(4),
BUCK_VSEL_MASK, 0xab, 0xfe },
/* BUCK 5 */
{ 500000, 5000, P1_BUCK_VSEL(5), P1_BUCK_SVSEL(5), P1_BUCK_CTRL(5),
BUCK_VSEL_MASK, 0x00, 0xaa },
{ 1375000, 25000, P1_BUCK_VSEL(5), P1_BUCK_SVSEL(5), P1_BUCK_CTRL(5),
BUCK_VSEL_MASK, 0xab, 0xfe },
/* BUCK 6 */
{ 500000, 5000, P1_BUCK_VSEL(6), P1_BUCK_SVSEL(6), P1_BUCK_CTRL(6),
BUCK_VSEL_MASK, 0x00, 0xaa },
{ 1375000, 25000, P1_BUCK_VSEL(6), P1_BUCK_SVSEL(6), P1_BUCK_CTRL(6),
BUCK_VSEL_MASK, 0xab, 0xfe },
};
static const struct p1_reg_info p1_aldos[] = {
/* ALDO 1 */
{ 500000, 25000, P1_ALDO_VOLT(1), P1_ALDO_SVOLT(1), P1_ALDO_CTRL(1),
ALDO_VSEL_MASK, 0x0b, 0x7f },
/* ALDO 2 */
{ 500000, 25000, P1_ALDO_VOLT(2), P1_ALDO_SVOLT(2), P1_ALDO_CTRL(2),
ALDO_VSEL_MASK, 0x0b, 0x7f },
/* ALDO 3 */
{ 500000, 25000, P1_ALDO_VOLT(3), P1_ALDO_SVOLT(3), P1_ALDO_CTRL(3),
ALDO_VSEL_MASK, 0x0b, 0x7f },
/* ALDO 4 */
{ 500000, 25000, P1_ALDO_VOLT(4), P1_ALDO_SVOLT(4), P1_ALDO_CTRL(4),
ALDO_VSEL_MASK, 0x0b, 0x7f },
};
static const struct p1_reg_info p1_dldos[] = {
/* DLDO 1 */
{ 500000, 25000, P1_DLDO_VOLT(1), P1_DLDO_SVOLT(1), P1_DLDO_CTRL(1),
ALDO_VSEL_MASK, 0x0b, 0x7f },
/* DLDO 2 */
{ 500000, 25000, P1_DLDO_VOLT(2), P1_DLDO_SVOLT(2), P1_DLDO_CTRL(2),
ALDO_VSEL_MASK, 0x0b, 0x7f },
/* DLDO 3 */
{ 500000, 25000, P1_DLDO_VOLT(3), P1_DLDO_SVOLT(3), P1_DLDO_CTRL(3),
ALDO_VSEL_MASK, 0x0b, 0x7f },
/* DLDO 4 */
{ 500000, 25000, P1_DLDO_VOLT(4), P1_DLDO_SVOLT(4), P1_DLDO_CTRL(4),
ALDO_VSEL_MASK, 0x0b, 0x7f },
/* DLDO 5 */
{ 500000, 25000, P1_DLDO_VOLT(5), P1_DLDO_SVOLT(5), P1_DLDO_CTRL(5),
ALDO_VSEL_MASK, 0x0b, 0x7f },
/* DLDO 6 */
{ 500000, 25000, P1_DLDO_VOLT(6), P1_DLDO_SVOLT(6), P1_DLDO_CTRL(6),
ALDO_VSEL_MASK, 0x0b, 0x7f },
/* DLDO 7 */
{ 500000, 25000, P1_DLDO_VOLT(7), P1_DLDO_SVOLT(7), P1_DLDO_CTRL(7),
ALDO_VSEL_MASK, 0x0b, 0x7f },
};
static const struct p1_reg_info *get_buck_reg(struct udevice *pmic,
int idx, int uvolt)
{
if (idx < 0)
return NULL;
if (uvolt < 1375000)
return &p1_bucks[(idx - 1) * 2 + 0];
return &p1_bucks[(idx - 1) * 2 + 1];
}
static const struct p1_reg_info *get_aldo_reg(struct udevice *pmic,
int idx, int uvolt)
{
if (idx < 1 || idx > ARRAY_SIZE(p1_aldos))
return NULL;
return &p1_aldos[idx - 1];
}
static const struct p1_reg_info *get_dldo_reg(struct udevice *pmic,
int idx, int uvolt)
{
if (idx < 1 || idx > ARRAY_SIZE(p1_dldos))
return NULL;
return &p1_dldos[idx - 1];
}
static int buck_get_value(struct udevice *dev)
{
const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
const struct p1_reg_info *info;
uint val;
int ret;
if (!ops || !ops->read)
return -ENOSYS;
info = get_buck_reg(dev->parent, dev->driver_data, 0);
if (!info)
return -ENOENT;
ret = pmic_reg_read(dev->parent, info->vsel_reg);
if (ret < 0)
return ret;
val = ret & info->vsel_mask;
while (val > info->max_sel)
info++;
return info->min_uv + (val - info->min_sel) * info->step_uv;
}
static int buck_set_value(struct udevice *dev, int uvolt)
{
const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
const struct p1_reg_info *info;
uint val;
int ret;
if (!ops || !ops->write)
return -ENOSYS;
info = get_buck_reg(dev->parent, dev->driver_data, uvolt);
if (!info)
return -ENOENT;
val = (uvolt - info->min_uv);
val = val / info->step_uv;
val += info->min_sel;
ret = pmic_reg_write(dev->parent, info->vsel_reg, val);
if (ret < 0)
return ret;
return 0;
}
static int buck_get_enable(struct udevice *dev)
{
const struct p1_reg_info *info;
int ret;
info = get_buck_reg(dev->parent, dev->driver_data, 0);
if (!info)
return -ENOENT;
ret = pmic_reg_read(dev->parent, info->config_reg);
if (ret < 0)
return ret;
return ret & BUCK_EN_MASK;
}
static int buck_set_enable(struct udevice *dev, bool enable)
{
const struct p1_reg_info *info;
uint val;
int ret;
info = get_buck_reg(dev->parent, dev->driver_data, 0);
if (!info)
return -ENOENT;
ret = pmic_reg_read(dev->parent, info->config_reg);
if (ret < 0)
return ret;
val = (unsigned int)ret;
val &= BUCK_EN_MASK;
if (enable == val)
return 0;
val = enable;
ret = pmic_clrsetbits(dev->parent, info->config_reg, BUCK_EN_MASK, val);
if (ret < 0)
return ret;
return 0;
}
static const struct dm_regulator_ops p1_buck_ops = {
.get_value = buck_get_value,
.set_value = buck_set_value,
.get_enable = buck_get_enable,
.set_enable = buck_set_enable,
};
static int p1_buck_probe(struct udevice *dev)
{
struct dm_regulator_uclass_plat *uc_pdata;
uc_pdata = dev_get_uclass_plat(dev);
uc_pdata->type = REGULATOR_TYPE_BUCK;
uc_pdata->mode_count = 0;
return 0;
}
U_BOOT_DRIVER(p1_buck) = {
.name = P1_BUCK_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &p1_buck_ops,
.probe = p1_buck_probe,
};
static int aldo_get_value(struct udevice *dev)
{
const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
const struct p1_reg_info *info;
uint val;
int ret;
if (!ops || !ops->read)
return -ENOSYS;
info = get_aldo_reg(dev->parent, dev->driver_data, 0);
if (!info)
return -ENOENT;
ret = pmic_reg_read(dev->parent, info->vsel_reg);
if (ret < 0)
return ret;
val = ret & info->vsel_mask;
while (val > info->max_sel)
info++;
return info->min_uv + (val - info->min_sel) * info->step_uv;
}
static int aldo_set_value(struct udevice *dev, int uvolt)
{
const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
const struct p1_reg_info *info;
uint val;
int ret;
if (!ops || !ops->write)
return -ENOSYS;
info = get_aldo_reg(dev->parent, dev->driver_data, uvolt);
if (!info)
return -ENOENT;
val = (uvolt - info->min_uv);
val = val / info->step_uv;
val += info->min_sel;
ret = pmic_reg_write(dev->parent, info->vsel_reg, val);
if (ret < 0)
return ret;
return 0;
}
static int aldo_get_enable(struct udevice *dev)
{
const struct p1_reg_info *info;
int ret;
info = get_aldo_reg(dev->parent, dev->driver_data, 0);
if (!info)
return -ENOENT;
ret = pmic_reg_read(dev->parent, info->config_reg);
if (ret < 0)
return ret;
return ret & ALDO_EN_MASK;
}
static int aldo_set_enable(struct udevice *dev, bool enable)
{
const struct p1_reg_info *info;
uint val;
int ret;
info = get_aldo_reg(dev->parent, dev->driver_data, 0);
if (!info)
return -ENOENT;
ret = pmic_reg_read(dev->parent, info->config_reg);
if (ret < 0)
return ret;
val = (unsigned int)ret;
val &= ALDO_EN_MASK;
if (enable == val)
return 0;
val = enable;
ret = pmic_clrsetbits(dev->parent, info->config_reg, ALDO_EN_MASK, val);
if (ret < 0)
return ret;
return 0;
}
static const struct dm_regulator_ops p1_aldo_ops = {
.get_value = aldo_get_value,
.set_value = aldo_set_value,
.get_enable = aldo_get_enable,
.set_enable = aldo_set_enable,
};
static int p1_aldo_probe(struct udevice *dev)
{
struct dm_regulator_uclass_plat *uc_pdata;
uc_pdata = dev_get_uclass_plat(dev);
uc_pdata->type = REGULATOR_TYPE_LDO;
uc_pdata->mode_count = 0;
return 0;
}
U_BOOT_DRIVER(p1_aldo) = {
.name = P1_ALDO_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &p1_aldo_ops,
.probe = p1_aldo_probe,
};
static int dldo_get_value(struct udevice *dev)
{
const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
const struct p1_reg_info *info;
uint val;
int ret;
if (!ops || !ops->read)
return -ENOSYS;
info = get_dldo_reg(dev->parent, dev->driver_data, 0);
if (!info)
return -ENOENT;
ret = pmic_reg_read(dev->parent, info->vsel_reg);
if (ret < 0)
return ret;
val = ret & info->vsel_mask;
while (val > info->max_sel)
info++;
return info->min_uv + (val - info->min_sel) * info->step_uv;
}
static int dldo_set_value(struct udevice *dev, int uvolt)
{
const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
const struct p1_reg_info *info;
uint val;
int ret;
if (!ops || !ops->write)
return -ENOSYS;
info = get_dldo_reg(dev->parent, dev->driver_data, uvolt);
if (!info)
return -ENOENT;
val = (uvolt - info->min_uv);
val = val / info->step_uv;
val += info->min_sel;
ret = pmic_reg_write(dev->parent, info->vsel_reg, val);
if (ret < 0)
return ret;
return 0;
}
static int dldo_get_enable(struct udevice *dev)
{
const struct p1_reg_info *info;
int ret;
info = get_dldo_reg(dev->parent, dev->driver_data, 0);
if (!info)
return -ENOENT;
ret = pmic_reg_read(dev->parent, info->config_reg);
if (ret < 0)
return ret;
return ret & DLDO_EN_MASK;
}
static int dldo_set_enable(struct udevice *dev, bool enable)
{
const struct p1_reg_info *info;
uint val;
int ret;
info = get_dldo_reg(dev->parent, dev->driver_data, 0);
if (!info)
return -ENOENT;
ret = pmic_reg_read(dev->parent, info->config_reg);
if (ret < 0)
return ret;
val = (unsigned int)ret;
val &= DLDO_EN_MASK;
if (enable == val)
return 0;
val = enable;
ret = pmic_clrsetbits(dev->parent, info->config_reg, DLDO_EN_MASK, val);
if (ret < 0)
return ret;
return 0;
}
static const struct dm_regulator_ops p1_dldo_ops = {
.get_value = dldo_get_value,
.set_value = dldo_set_value,
.get_enable = dldo_get_enable,
.set_enable = dldo_set_enable,
};
static int p1_dldo_probe(struct udevice *dev)
{
struct dm_regulator_uclass_plat *uc_pdata;
uc_pdata = dev_get_uclass_plat(dev);
uc_pdata->type = REGULATOR_TYPE_LDO;
uc_pdata->mode_count = 0;
return 0;
}
U_BOOT_DRIVER(p1_dldo) = {
.name = P1_DLDO_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &p1_dldo_ops,
.probe = p1_dldo_probe,
};
-7
View File
@@ -279,12 +279,5 @@ config RESET_RZG2L_USBPHY_CTRL
RZ/G2L SoC. This is required for USB 2.0 functionality to work on this
SoC.
config RESET_SPACEMIT_K1
bool "Support for SPACEMIT's K1 Reset driver"
depends on DM_RESET
help
Support for SPACEMIT's K1 Reset system. Basic Assert/Deassert
is supported.
source "drivers/reset/stm32/Kconfig"
endmenu
+1 -1
View File
@@ -37,7 +37,7 @@ obj-$(CONFIG_RESET_DRA7) += reset-dra7.o
obj-$(CONFIG_RESET_AT91) += reset-at91.o
obj-$(CONFIG_$(PHASE_)RESET_JH7110) += reset-jh7110.o
obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o
obj-$(CONFIG_CLK_SPACEMIT_K1) += spacemit/
obj-$(CONFIG_ARCH_STM32) += stm32/
obj-$(CONFIG_ARCH_STM32MP) += stm32/
-548
View File
@@ -1,548 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2022 Spacemit Inc.
* Copyright (C) 2025 Huan Zhou <pericycle.cc@gmail.com>
*/
#include <asm/io.h>
#include <config.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dt-bindings/reset/spacemit-k1-reset.h>
#include <linux/bitops.h>
#include <reset-uclass.h>
/* APBC register offset */
#define APBC_UART1_CLK_RST 0x0
#define APBC_UART2_CLK_RST 0x4
#define APBC_GPIO_CLK_RST 0x8
#define APBC_PWM0_CLK_RST 0xc
#define APBC_PWM1_CLK_RST 0x10
#define APBC_PWM2_CLK_RST 0x14
#define APBC_PWM3_CLK_RST 0x18
#define APBC_TWSI8_CLK_RST 0x20
#define APBC_UART3_CLK_RST 0x24
#define APBC_RTC_CLK_RST 0x28
#define APBC_TWSI0_CLK_RST 0x2c
#define APBC_TWSI1_CLK_RST 0x30
#define APBC_TIMERS1_CLK_RST 0x34
#define APBC_TWSI2_CLK_RST 0x38
#define APBC_AIB_CLK_RST 0x3c
#define APBC_TWSI4_CLK_RST 0x40
#define APBC_TIMERS2_CLK_RST 0x44
#define APBC_ONEWIRE_CLK_RST 0x48
#define APBC_TWSI5_CLK_RST 0x4c
#define APBC_DRO_CLK_RST 0x58
#define APBC_IR_CLK_RST 0x5c
#define APBC_TWSI6_CLK_RST 0x60
#define APBC_TWSI7_CLK_RST 0x68
#define APBC_TSEN_CLK_RST 0x6c
#define APBC_UART4_CLK_RST 0x70
#define APBC_UART5_CLK_RST 0x74
#define APBC_UART6_CLK_RST 0x78
#define APBC_SSP3_CLK_RST 0x7c
#define APBC_SSPA0_CLK_RST 0x80
#define APBC_SSPA1_CLK_RST 0x84
#define APBC_IPC_AP2AUD_CLK_RST 0x90
#define APBC_UART7_CLK_RST 0x94
#define APBC_UART8_CLK_RST 0x98
#define APBC_UART9_CLK_RST 0x9c
#define APBC_CAN0_CLK_RST 0xa0
#define APBC_PWM4_CLK_RST 0xa8
#define APBC_PWM5_CLK_RST 0xac
#define APBC_PWM6_CLK_RST 0xb0
#define APBC_PWM7_CLK_RST 0xb4
#define APBC_PWM8_CLK_RST 0xb8
#define APBC_PWM9_CLK_RST 0xbc
#define APBC_PWM10_CLK_RST 0xc0
#define APBC_PWM11_CLK_RST 0xc4
#define APBC_PWM12_CLK_RST 0xc8
#define APBC_PWM13_CLK_RST 0xcc
#define APBC_PWM14_CLK_RST 0xd0
#define APBC_PWM15_CLK_RST 0xd4
#define APBC_PWM16_CLK_RST 0xd8
#define APBC_PWM17_CLK_RST 0xdc
#define APBC_PWM18_CLK_RST 0xe0
#define APBC_PWM19_CLK_RST 0xe4
/* end of APBC register offset */
/* MPMU register offset */
#define MPMU_WDTPCR 0x200
/* end of MPMU register offset */
/* APMU register offset */
#define APMU_JPG_CLK_RES_CTRL 0x20
#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x24
#define APMU_ISP_CLK_RES_CTRL 0x38
#define APMU_LCD_CLK_RES_CTRL1 0x44
#define APMU_LCD_SPI_CLK_RES_CTRL 0x48
#define APMU_LCD_CLK_RES_CTRL2 0x4c
#define APMU_CCIC_CLK_RES_CTRL 0x50
#define APMU_SDH0_CLK_RES_CTRL 0x54
#define APMU_SDH1_CLK_RES_CTRL 0x58
#define APMU_USB_CLK_RES_CTRL 0x5c
#define APMU_QSPI_CLK_RES_CTRL 0x60
#define APMU_USB_CLK_RES_CTRL 0x5c
#define APMU_DMA_CLK_RES_CTRL 0x64
#define APMU_AES_CLK_RES_CTRL 0x68
#define APMU_VPU_CLK_RES_CTRL 0xa4
#define APMU_GPU_CLK_RES_CTRL 0xcc
#define APMU_SDH2_CLK_RES_CTRL 0xe0
#define APMU_PMUA_MC_CTRL 0xe8
#define APMU_PMU_CC2_AP 0x100
#define APMU_PMUA_EM_CLK_RES_CTRL 0x104
#define APMU_AUDIO_CLK_RES_CTRL 0x14c
#define APMU_HDMI_CLK_RES_CTRL 0x1B8
#define APMU_PCIE_CLK_RES_CTRL_0 0x3cc
#define APMU_PCIE_CLK_RES_CTRL_1 0x3d4
#define APMU_PCIE_CLK_RES_CTRL_2 0x3dc
#define APMU_EMAC0_CLK_RES_CTRL 0x3e4
#define APMU_EMAC1_CLK_RES_CTRL 0x3ec
/* end of APMU register offset */
/* APBC2 register offset */
#define APBC2_UART1_CLK_RST 0x00
#define APBC2_SSP2_CLK_RST 0x04
#define APBC2_TWSI3_CLK_RST 0x08
#define APBC2_RTC_CLK_RST 0x0c
#define APBC2_TIMERS0_CLK_RST 0x10
#define APBC2_KPC_CLK_RST 0x14
#define APBC2_GPIO_CLK_RST 0x1c
/* end of APBC2 register offset */
enum spacemit_reset_base_type {
RST_BASE_TYPE_MPMU = 0,
RST_BASE_TYPE_APMU = 1,
RST_BASE_TYPE_APBC = 2,
RST_BASE_TYPE_APBS = 3,
RST_BASE_TYPE_CIU = 4,
RST_BASE_TYPE_DCIU = 5,
RST_BASE_TYPE_DDRC = 6,
RST_BASE_TYPE_AUDC = 7,
RST_BASE_TYPE_APBC2 = 8,
};
struct spacemit_reset_signal {
u32 offset;
u32 mask;
u32 deassert_val;
u32 assert_val;
enum spacemit_reset_base_type type;
};
struct spacemit_reset_base {
void __iomem *mpmu_base;
void __iomem *apmu_base;
void __iomem *apbc_base;
void __iomem *apbs_base;
void __iomem *ciu_base;
void __iomem *dciu_base;
void __iomem *ddrc_base;
void __iomem *audio_ctrl_base;
void __iomem *apbc2_base;
};
struct spacemit_reset {
struct spacemit_reset_base io_base;
const struct spacemit_reset_signal *signals;
};
enum {
RESET_TWSI6_SPL = 0,
RESET_TWSI8_SPL,
RESET_SDH_AXI_SPL,
RESET_SDH0_SPL,
RESET_USB_AXI_SPL,
RESET_USBP1_AXI_SPL,
RESET_USB3_0_SPL,
RESET_QSPI_SPL,
RESET_QSPI_BUS_SPL,
RESET_AES_SPL,
RESET_SDH2_SPL,
RESET_NUMBER_SPL
};
static const struct spacemit_reset_signal
k1_reset_signals[RESET_NUMBER] = {
[RESET_UART1] = { APBC_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_UART2] = { APBC_UART2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_GPIO] = { APBC_GPIO_CLK_RST, BIT(2), 0,
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM0] = { APBC_PWM0_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM1] = { APBC_PWM1_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM2] = { APBC_PWM2_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM3] = { APBC_PWM3_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM4] = { APBC_PWM4_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM5] = { APBC_PWM5_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM6] = { APBC_PWM6_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM7] = { APBC_PWM7_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM8] = { APBC_PWM8_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM9] = { APBC_PWM9_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM10] = { APBC_PWM10_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM11] = { APBC_PWM11_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM12] = { APBC_PWM12_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM13] = { APBC_PWM13_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM14] = { APBC_PWM14_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM15] = { APBC_PWM15_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM16] = { APBC_PWM16_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM17] = { APBC_PWM17_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM18] = { APBC_PWM18_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_PWM19] = { APBC_PWM19_CLK_RST,
BIT(2) | BIT(0),
BIT(0),
BIT(2),
RST_BASE_TYPE_APBC },
[RESET_SSP3] = { APBC_SSP3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_UART3] = { APBC_UART3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_RTC] = { APBC_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TWSI0] = { APBC_TWSI0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TIMERS1] = { APBC_TIMERS1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_AIB] = { APBC_AIB_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TIMERS2] = { APBC_TIMERS2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_ONEWIRE] = { APBC_ONEWIRE_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_SSPA0] = { APBC_SSPA0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_SSPA1] = { APBC_SSPA1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_DRO] = { APBC_DRO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_IR] = { APBC_IR_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TWSI1] = { APBC_TWSI1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TSEN] = { APBC_TSEN_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TWSI2] = { APBC_TWSI2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TWSI4] = { APBC_TWSI4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TWSI5] = { APBC_TWSI5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TWSI6] = { APBC_TWSI6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TWSI7] = { APBC_TWSI7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_TWSI8] = { APBC_TWSI8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_IPC_AP2AUD] = { APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_UART4] = { APBC_UART4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_UART5] = { APBC_UART5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_UART6] = { APBC_UART6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_UART7] = { APBC_UART7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_UART8] = { APBC_UART8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_UART9] = { APBC_UART9_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
[RESET_CAN0] = { APBC_CAN0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
/* MPMU */
[RESET_WDT] = { MPMU_WDTPCR, BIT(2), 0, BIT(2), RST_BASE_TYPE_MPMU },
/* APMU */
[RESET_JPG] = { APMU_JPG_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_CSI] = { APMU_CSI_CCIC2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_CCIC2_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL,
BIT(2),
BIT(2),
0,
RST_BASE_TYPE_APMU },
[RESET_CCIC3_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL,
BIT(29),
BIT(29),
0,
RST_BASE_TYPE_APMU },
[RESET_ISP] = { APMU_ISP_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_ISP_AHB] = { APMU_ISP_CLK_RES_CTRL, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU },
[RESET_ISP_CI] = { APMU_ISP_CLK_RES_CTRL, BIT(16), BIT(16), 0, RST_BASE_TYPE_APMU },
[RESET_ISP_CPP] = { APMU_ISP_CLK_RES_CTRL, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU },
[RESET_LCD] = { APMU_LCD_CLK_RES_CTRL1, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU },
[RESET_DSI_ESC] = { APMU_LCD_CLK_RES_CTRL1, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU },
[RESET_V2D] = { APMU_LCD_CLK_RES_CTRL1, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU },
[RESET_MIPI] = { APMU_LCD_CLK_RES_CTRL1, BIT(15), BIT(15), 0, RST_BASE_TYPE_APMU },
[RESET_LCD_SPI] = { APMU_LCD_SPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_LCD_SPI_BUS] = { APMU_LCD_SPI_CLK_RES_CTRL,
BIT(4),
BIT(4),
0,
RST_BASE_TYPE_APMU },
[RESET_LCD_SPI_HBUS] = { APMU_LCD_SPI_CLK_RES_CTRL,
BIT(2),
BIT(2),
0,
RST_BASE_TYPE_APMU },
[RESET_LCD_MCLK] = { APMU_LCD_CLK_RES_CTRL2, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU },
[RESET_CCIC_4X] = { APMU_CCIC_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_CCIC1_PHY] = { APMU_CCIC_CLK_RES_CTRL, BIT(2), BIT(2), 0, RST_BASE_TYPE_APMU },
[RESET_SDH_AXI] = { APMU_SDH0_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_SDH0] = { APMU_SDH0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_SDH1] = { APMU_SDH1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_USB_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_USBP1_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU },
[RESET_USB3_0] = { APMU_USB_CLK_RES_CTRL,
BIT(9) | BIT(10) | BIT(11),
BIT(9) | BIT(10) | BIT(11),
0,
RST_BASE_TYPE_APMU },
[RESET_QSPI] = { APMU_QSPI_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_QSPI_BUS] = { APMU_QSPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_DMA] = { APMU_DMA_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_AES] = { APMU_AES_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU },
[RESET_VPU] = { APMU_VPU_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_GPU] = { APMU_GPU_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_SDH2] = { APMU_SDH2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_MC] = { APMU_PMUA_MC_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_EM_AXI] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
[RESET_EM] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_AUDIO_SYS] = { APMU_AUDIO_CLK_RES_CTRL,
BIT(0) | BIT(2) | BIT(3),
BIT(0) | BIT(2) | BIT(3),
0,
RST_BASE_TYPE_APMU },
[RESET_HDMI] = { APMU_HDMI_CLK_RES_CTRL, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU },
[RESET_PCIE0] = { APMU_PCIE_CLK_RES_CTRL_0,
BIT(3) | BIT(4) | BIT(5) | BIT(8),
BIT(3) | BIT(4) | BIT(5),
BIT(8),
RST_BASE_TYPE_APMU },
[RESET_PCIE1] = { APMU_PCIE_CLK_RES_CTRL_1,
BIT(3) | BIT(4) | BIT(5) | BIT(8),
BIT(3) | BIT(4) | BIT(5),
BIT(8),
RST_BASE_TYPE_APMU },
[RESET_PCIE2] = { APMU_PCIE_CLK_RES_CTRL_2, 0x138, 0x38, 0x100, RST_BASE_TYPE_APMU },
[RESET_EMAC0] = { APMU_EMAC0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_EMAC1] = { APMU_EMAC1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
[RESET_SEC_UART1] = { APBC2_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
[RESET_SEC_SSP2] = { APBC2_SSP2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
[RESET_SEC_TWSI3] = { APBC2_TWSI3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
[RESET_SEC_RTC] = { APBC2_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
[RESET_SEC_TIMERS0] = { APBC2_TIMERS0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
[RESET_SEC_KPC] = { APBC2_KPC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
[RESET_SEC_GPIO] = { APBC2_GPIO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
};
static u32 spacemit_reset_read(struct spacemit_reset *reset, u32 id)
{
void __iomem *base;
switch (reset->signals[id].type) {
case RST_BASE_TYPE_APMU:
base = reset->io_base.apmu_base;
break;
case RST_BASE_TYPE_APBC:
base = reset->io_base.apbc_base;
break;
default:
base = reset->io_base.apbc_base;
break;
}
return readl(base + reset->signals[id].offset);
}
static void spacemit_reset_write(struct spacemit_reset *reset, u32 value, u32 id)
{
void __iomem *base;
switch (reset->signals[id].type) {
case RST_BASE_TYPE_APMU:
base = reset->io_base.apmu_base;
break;
case RST_BASE_TYPE_APBC:
base = reset->io_base.apbc_base;
break;
default:
base = reset->io_base.apbc_base;
break;
}
writel(value, base + reset->signals[id].offset);
}
static void spacemit_reset_set(struct reset_ctl *rst, u32 id, bool assert)
{
u32 value;
struct spacemit_reset *reset = dev_get_priv(rst->dev);
value = spacemit_reset_read(reset, id);
if (assert) {
value &= ~reset->signals[id].mask;
value |= reset->signals[id].assert_val;
} else {
value &= ~reset->signals[id].mask;
value |= reset->signals[id].deassert_val;
}
spacemit_reset_write(reset, value, id);
}
static int spacemit_reset_update(struct reset_ctl *rst, bool assert)
{
if (rst->id < RESET_UART1 || rst->id >= RESET_NUMBER)
return 0;
/* can not write to twsi8 */
if (rst->id == RESET_TWSI8)
return 0;
spacemit_reset_set(rst, rst->id, assert);
return 0;
}
static int spacemit_reset_assert(struct reset_ctl *rst)
{
return spacemit_reset_update(rst, true);
}
static int spacemit_reset_deassert(struct reset_ctl *rst)
{
return spacemit_reset_update(rst, false);
}
static int spacemit_k1_reset_probe(struct udevice *dev)
{
struct spacemit_reset *reset = dev_get_priv(dev);
reset->io_base.mpmu_base = (void *)dev_remap_addr_index(dev, 0);
if (!reset->io_base.mpmu_base) {
pr_err("failed to map mpmu registers\n");
goto out;
}
reset->io_base.apmu_base = (void *)dev_remap_addr_index(dev, 1);
if (!reset->io_base.apmu_base) {
pr_err("failed to map apmu registers\n");
goto out;
}
reset->io_base.apbc_base = (void *)dev_remap_addr_index(dev, 2);
if (!reset->io_base.apbc_base) {
pr_err("failed to map apbc registers\n");
goto out;
}
reset->io_base.apbs_base = (void *)dev_remap_addr_index(dev, 3);
if (!reset->io_base.apbs_base) {
pr_err("failed to map apbs registers\n");
goto out;
}
reset->io_base.ciu_base = (void *)dev_remap_addr_index(dev, 4);
if (!reset->io_base.ciu_base) {
pr_err("failed to map ciu registers\n");
goto out;
}
reset->io_base.dciu_base = (void *)dev_remap_addr_index(dev, 5);
if (!reset->io_base.dciu_base) {
pr_err("failed to map dragon ciu registers\n");
goto out;
}
reset->io_base.ddrc_base = (void *)dev_remap_addr_index(dev, 6);
if (!reset->io_base.ddrc_base) {
pr_err("failed to map ddrc registers\n");
goto out;
}
reset->io_base.apbc2_base = (void *)dev_remap_addr_index(dev, 7);
if (!reset->io_base.apbc2_base) {
pr_err("failed to map apbc2 registers\n");
goto out;
}
reset->signals = k1_reset_signals;
out:
return 0;
}
const struct reset_ops k1_reset_ops = {
.rst_assert = spacemit_reset_assert,
.rst_deassert = spacemit_reset_deassert,
};
static const struct udevice_id k1_reset_ids[] = {
{ .compatible = "spacemit,k1-reset", },
{},
};
U_BOOT_DRIVER(k1_reset) = {
.name = "spacemit,k1-reset",
.id = UCLASS_RESET,
.ops = &k1_reset_ops,
.of_match = k1_reset_ids,
.probe = spacemit_k1_reset_probe,
.priv_auto = sizeof(struct spacemit_reset),
};
+5
View File
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
#
# SpacemiT reset drivers
obj-y += reset-spacemit-k1.o
+289
View File
@@ -0,0 +1,289 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* SpacemiT K1 reset driver (syscon-bound).
*
* Copyright (C) 2022 Spacemit Inc.
* Copyright (C) 2025 Huan Zhou <pericycle.cc@gmail.com>
* Copyright (C) 2026 RISCstar Ltd.
*/
#include <asm/io.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dt-bindings/clock/spacemit,k1-syscon.h>
#include <linux/bitops.h>
#include <malloc.h>
#include <reset-uclass.h>
#include <soc/spacemit/k1-reset.h>
#include <soc/spacemit/k1-syscon.h>
/* ===================================================================
* Per-syscon reset signal tables.
*
* Indexed by the kernel-side per-syscon-local IDs from
* <dt-bindings/clock/spacemit,k1-syscon.h>. Each entry is
* (offset, assert_mask, deassert_mask): bits in assert_mask are set
* when the reset line is asserted; bits in deassert_mask are set when
* deasserted; the union (assert_mask | deassert_mask) is the set of
* bits the controller will overwrite on each transition.
*
* Layout mirrors the kernel-side K1 reset driver.
* ===================================================================
*/
struct spacemit_k1_reset_data {
u32 offset;
u32 assert_mask;
u32 deassert_mask;
};
#define RESET_DATA(o, a, d) { \
.offset = (o), .assert_mask = (a), .deassert_mask = (d) \
}
static const struct spacemit_k1_reset_data k1_mpmu_resets[] = {
[RESET_WDT] = RESET_DATA(MPMU_WDTPCR, BIT(2), 0),
};
static const struct spacemit_k1_reset_data k1_apbc_resets[] = {
[RESET_UART0] = RESET_DATA(APBC_UART1_CLK_RST, BIT(2), 0),
[RESET_UART2] = RESET_DATA(APBC_UART2_CLK_RST, BIT(2), 0),
[RESET_UART3] = RESET_DATA(APBC_UART3_CLK_RST, BIT(2), 0),
[RESET_UART4] = RESET_DATA(APBC_UART4_CLK_RST, BIT(2), 0),
[RESET_UART5] = RESET_DATA(APBC_UART5_CLK_RST, BIT(2), 0),
[RESET_UART6] = RESET_DATA(APBC_UART6_CLK_RST, BIT(2), 0),
[RESET_UART7] = RESET_DATA(APBC_UART7_CLK_RST, BIT(2), 0),
[RESET_UART8] = RESET_DATA(APBC_UART8_CLK_RST, BIT(2), 0),
[RESET_UART9] = RESET_DATA(APBC_UART9_CLK_RST, BIT(2), 0),
[RESET_GPIO] = RESET_DATA(APBC_GPIO_CLK_RST, BIT(2), 0),
[RESET_PWM0] = RESET_DATA(APBC_PWM0_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM1] = RESET_DATA(APBC_PWM1_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM2] = RESET_DATA(APBC_PWM2_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM3] = RESET_DATA(APBC_PWM3_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM4] = RESET_DATA(APBC_PWM4_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM5] = RESET_DATA(APBC_PWM5_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM6] = RESET_DATA(APBC_PWM6_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM7] = RESET_DATA(APBC_PWM7_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM8] = RESET_DATA(APBC_PWM8_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM9] = RESET_DATA(APBC_PWM9_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM10] = RESET_DATA(APBC_PWM10_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM11] = RESET_DATA(APBC_PWM11_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM12] = RESET_DATA(APBC_PWM12_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM13] = RESET_DATA(APBC_PWM13_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM14] = RESET_DATA(APBC_PWM14_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM15] = RESET_DATA(APBC_PWM15_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM16] = RESET_DATA(APBC_PWM16_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM17] = RESET_DATA(APBC_PWM17_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM18] = RESET_DATA(APBC_PWM18_CLK_RST, BIT(2), BIT(0)),
[RESET_PWM19] = RESET_DATA(APBC_PWM19_CLK_RST, BIT(2), BIT(0)),
[RESET_SSP3] = RESET_DATA(APBC_SSP3_CLK_RST, BIT(2), 0),
[RESET_RTC] = RESET_DATA(APBC_RTC_CLK_RST, BIT(2), 0),
[RESET_TWSI0] = RESET_DATA(APBC_TWSI0_CLK_RST, BIT(2), 0),
[RESET_TWSI1] = RESET_DATA(APBC_TWSI1_CLK_RST, BIT(2), 0),
[RESET_TWSI2] = RESET_DATA(APBC_TWSI2_CLK_RST, BIT(2), 0),
[RESET_TWSI4] = RESET_DATA(APBC_TWSI4_CLK_RST, BIT(2), 0),
[RESET_TWSI5] = RESET_DATA(APBC_TWSI5_CLK_RST, BIT(2), 0),
[RESET_TWSI6] = RESET_DATA(APBC_TWSI6_CLK_RST, BIT(2), 0),
[RESET_TWSI7] = RESET_DATA(APBC_TWSI7_CLK_RST, BIT(2), 0),
[RESET_TWSI8] = RESET_DATA(APBC_TWSI8_CLK_RST, BIT(2), 0),
[RESET_TIMERS1] = RESET_DATA(APBC_TIMERS1_CLK_RST, BIT(2), 0),
[RESET_TIMERS2] = RESET_DATA(APBC_TIMERS2_CLK_RST, BIT(2), 0),
[RESET_AIB] = RESET_DATA(APBC_AIB_CLK_RST, BIT(2), 0),
[RESET_ONEWIRE] = RESET_DATA(APBC_ONEWIRE_CLK_RST, BIT(2), 0),
[RESET_SSPA0] = RESET_DATA(APBC_SSPA0_CLK_RST, BIT(2), 0),
[RESET_SSPA1] = RESET_DATA(APBC_SSPA1_CLK_RST, BIT(2), 0),
[RESET_DRO] = RESET_DATA(APBC_DRO_CLK_RST, BIT(2), 0),
[RESET_IR] = RESET_DATA(APBC_IR_CLK_RST, BIT(2), 0),
[RESET_TSEN] = RESET_DATA(APBC_TSEN_CLK_RST, BIT(2), 0),
[RESET_IPC_AP2AUD] = RESET_DATA(APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0),
[RESET_CAN0] = RESET_DATA(APBC_CAN0_CLK_RST, BIT(2), 0),
};
static const struct spacemit_k1_reset_data k1_apmu_resets[] = {
[RESET_CCIC_4X] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(1)),
[RESET_CCIC1_PHY] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(2)),
[RESET_SDH_AXI] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(0)),
[RESET_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)),
[RESET_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)),
[RESET_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)),
[RESET_USBP1_AXI] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(4)),
[RESET_USB_AXI] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(0)),
[RESET_USB30_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(9)),
[RESET_USB30_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(10)),
[RESET_USB30_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(11)),
[RESET_QSPI] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(1)),
[RESET_QSPI_BUS] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(0)),
[RESET_DMA] = RESET_DATA(APMU_DMA_CLK_RES_CTRL, 0, BIT(0)),
[RESET_AES] = RESET_DATA(APMU_AES_CLK_RES_CTRL, 0, BIT(4)),
[RESET_VPU] = RESET_DATA(APMU_VPU_CLK_RES_CTRL, 0, BIT(0)),
[RESET_GPU] = RESET_DATA(APMU_GPU_CLK_RES_CTRL, 0, BIT(1)),
[RESET_EMMC] = RESET_DATA(APMU_PMUA_EM_CLK_RES_CTRL, 0, BIT(1)),
[RESET_EMMC_X] = RESET_DATA(APMU_PMUA_EM_CLK_RES_CTRL, 0, BIT(0)),
[RESET_AUDIO_SYS] = RESET_DATA(APMU_AUDIO_CLK_RES_CTRL, 0, BIT(0)),
[RESET_AUDIO_MCU] = RESET_DATA(APMU_AUDIO_CLK_RES_CTRL, 0, BIT(2)),
[RESET_AUDIO_APMU] = RESET_DATA(APMU_AUDIO_CLK_RES_CTRL, 0, BIT(3)),
[RESET_HDMI] = RESET_DATA(APMU_HDMI_CLK_RES_CTRL, 0, BIT(9)),
[RESET_PCIE0_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, 0, BIT(3)),
[RESET_PCIE0_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, 0, BIT(4)),
[RESET_PCIE0_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, 0, BIT(5)),
[RESET_PCIE0_GLOBAL] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, BIT(8), 0),
[RESET_PCIE1_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, 0, BIT(3)),
[RESET_PCIE1_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, 0, BIT(4)),
[RESET_PCIE1_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, 0, BIT(5)),
[RESET_PCIE1_GLOBAL] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, BIT(8), 0),
[RESET_PCIE2_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, 0, BIT(3)),
[RESET_PCIE2_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, 0, BIT(4)),
[RESET_PCIE2_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, 0, BIT(5)),
[RESET_PCIE2_GLOBAL] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, BIT(8), 0),
[RESET_EMAC0] = RESET_DATA(APMU_EMAC0_CLK_RES_CTRL, 0, BIT(1)),
[RESET_EMAC1] = RESET_DATA(APMU_EMAC1_CLK_RES_CTRL, 0, BIT(1)),
[RESET_JPG] = RESET_DATA(APMU_JPG_CLK_RES_CTRL, 0, BIT(0)),
[RESET_CCIC2PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(2)),
[RESET_CCIC3PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(29)),
[RESET_CSI] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(1)),
[RESET_ISP] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(0)),
[RESET_ISP_CPP] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(27)),
[RESET_ISP_BUS] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(3)),
[RESET_ISP_CI] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(16)),
[RESET_DPU_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL2, 0, BIT(9)),
[RESET_DPU_ESC] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(3)),
[RESET_DPU_HCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(4)),
[RESET_DPU_SPIBUS] = RESET_DATA(APMU_LCD_SPI_CLK_RES_CTRL, 0, BIT(4)),
[RESET_DPU_SPI_HBUS] = RESET_DATA(APMU_LCD_SPI_CLK_RES_CTRL, 0, BIT(2)),
[RESET_V2D] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(27)),
[RESET_MIPI] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(15)),
[RESET_MC] = RESET_DATA(APMU_PMUA_MC_CTRL, 0, BIT(0)),
};
static const struct spacemit_k1_reset_data k1_apbc2_resets[] = {
[RESET_APBC2_UART1] = RESET_DATA(APBC2_UART1_CLK_RST, BIT(2), 0),
[RESET_APBC2_SSP2] = RESET_DATA(APBC2_SSP2_CLK_RST, BIT(2), 0),
[RESET_APBC2_TWSI3] = RESET_DATA(APBC2_TWSI3_CLK_RST, BIT(2), 0),
[RESET_APBC2_RTC] = RESET_DATA(APBC2_RTC_CLK_RST, BIT(2), 0),
[RESET_APBC2_TIMERS0] = RESET_DATA(APBC2_TIMERS0_CLK_RST, BIT(2), 0),
[RESET_APBC2_KPC] = RESET_DATA(APBC2_KPC_CLK_RST, BIT(2), 0),
[RESET_APBC2_GPIO] = RESET_DATA(APBC2_GPIO_CLK_RST, BIT(2), 0),
};
/* ===================================================================
* Driver
* ===================================================================
*/
struct spacemit_k1_reset_priv {
void __iomem *base;
const struct spacemit_k1_reset_data *table;
size_t table_size;
};
static int spacemit_k1_reset_xfer(struct reset_ctl *rst, bool assert)
{
struct spacemit_k1_reset_priv *priv = dev_get_priv(rst->dev);
const struct spacemit_k1_reset_data *e;
u32 v;
if (rst->id >= priv->table_size)
return -EINVAL;
e = &priv->table[rst->id];
if (e->assert_mask == 0 && e->deassert_mask == 0)
return -EINVAL; /* not owned by this syscon */
v = readl(priv->base + e->offset);
v &= ~(e->assert_mask | e->deassert_mask);
v |= assert ? e->assert_mask : e->deassert_mask;
writel(v, priv->base + e->offset);
return 0;
}
static int spacemit_k1_reset_assert(struct reset_ctl *rst)
{
return spacemit_k1_reset_xfer(rst, true);
}
static int spacemit_k1_reset_deassert(struct reset_ctl *rst)
{
return spacemit_k1_reset_xfer(rst, false);
}
static int spacemit_k1_reset_request(struct reset_ctl *rst)
{
struct spacemit_k1_reset_priv *priv = dev_get_priv(rst->dev);
return rst->id < priv->table_size ? 0 : -EINVAL;
}
static const struct reset_ops spacemit_k1_reset_ops = {
.request = spacemit_k1_reset_request,
.rst_assert = spacemit_k1_reset_assert,
.rst_deassert = spacemit_k1_reset_deassert,
};
static int spacemit_k1_reset_probe(struct udevice *dev)
{
struct spacemit_k1_reset_priv *priv = dev_get_priv(dev);
priv->base = (void __iomem *)dev_remap_addr(dev);
if (!priv->base)
return -ENODEV;
return 0;
}
U_BOOT_DRIVER(spacemit_k1_reset) = {
.name = "spacemit_k1_reset",
.id = UCLASS_RESET,
.ops = &spacemit_k1_reset_ops,
.probe = spacemit_k1_reset_probe,
.priv_auto = sizeof(struct spacemit_k1_reset_priv),
.flags = DM_FLAG_PRE_RELOC,
};
int spacemit_k1_reset_bind(struct udevice *parent,
enum spacemit_k1_reset_syscon syscon)
{
struct spacemit_k1_reset_priv *priv;
struct udevice *rst_dev;
const struct spacemit_k1_reset_data *table;
size_t table_size;
int ret;
switch (syscon) {
case SPACEMIT_K1_RESET_MPMU:
table = k1_mpmu_resets;
table_size = ARRAY_SIZE(k1_mpmu_resets);
break;
case SPACEMIT_K1_RESET_APMU:
table = k1_apmu_resets;
table_size = ARRAY_SIZE(k1_apmu_resets);
break;
case SPACEMIT_K1_RESET_APBC:
table = k1_apbc_resets;
table_size = ARRAY_SIZE(k1_apbc_resets);
break;
case SPACEMIT_K1_RESET_APBC2:
table = k1_apbc2_resets;
table_size = ARRAY_SIZE(k1_apbc2_resets);
break;
default:
return -EINVAL;
}
ret = device_bind_driver_to_node(parent, "spacemit_k1_reset", "reset",
dev_ofnode(parent), &rst_dev);
if (ret)
return ret;
priv = malloc(sizeof(*priv));
if (!priv) {
device_unbind(rst_dev);
return -ENOMEM;
}
priv->table = table;
priv->table_size = table_size;
dev_set_priv(rst_dev, priv);
return 0;
}
+9
View File
@@ -267,6 +267,14 @@ static const struct fsl_qspi_devtype_data ls2080a_data = {
.little_endian = true,
};
static const struct fsl_qspi_devtype_data spacemit_k1_data = {
.rxfifo = SZ_128,
.txfifo = SZ_256,
.ahb_buf_size = SZ_128,
.quirks = QUADSPI_QUIRK_TKT253890,
.little_endian = true,
};
struct fsl_qspi {
struct udevice *dev;
void __iomem *iobase;
@@ -870,6 +878,7 @@ static const struct udevice_id fsl_qspi_ids[] = {
{ .compatible = "fsl,ls1021a-qspi", .data = (ulong)&ls1021a_data, },
{ .compatible = "fsl,ls1088a-qspi", .data = (ulong)&ls2080a_data, },
{ .compatible = "fsl,ls2080a-qspi", .data = (ulong)&ls2080a_data, },
{ .compatible = "spacemit,k1-qspi", .data = (ulong)&spacemit_k1_data, },
{ }
};
-13
View File
@@ -1,13 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2024, Kongyang Liu <seashell11234455@gmail.com>
*
*/
#ifndef __CONFIG_H
#define __CONFIG_H
#define CFG_SYS_SDRAM_BASE 0x0
#define CFG_SYS_NS16550_IER 0x40 /* UART Unit Enable */
#endif /* __CONFIG_H */
+20
View File
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2024, Kongyang Liu <seashell11234455@gmail.com>
* Copyright (C) 2025-2026, RISCstar Ltd.
*
*/
#ifndef __CONFIG_H
#define __CONFIG_H
#define CFG_SYS_NS16550_CLK 14700000
#define CFG_SYS_NS16550_IER 0x40 /* UART Unit Enable */
#define CFG_SYS_SDRAM_BASE 0x0
#define RISCV_MMODE_TIMERBASE 0xe4000000
#define RISCV_MMODE_TIMEROFF 0xbff8
#define RISCV_MMODE_TIMER_FREQ 24000000
#define RISCV_SMODE_TIMER_FREQ 24000000
#endif /* __CONFIG_H */
@@ -1,118 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2022 Spacemit Inc.
* Copyright (C) 2025 Huan Zhou <pericycle.cc@gmail.com>
*/
#ifndef __DT_BINDINGS_RESET_SAPCEMIT_K1_H__
#define __DT_BINDINGS_RESET_SAPCEMIT_K1_H__
/* APBC */
#define RESET_UART1 1
#define RESET_UART2 2
#define RESET_GPIO 3
#define RESET_PWM0 4
#define RESET_PWM1 5
#define RESET_PWM2 6
#define RESET_PWM3 7
#define RESET_PWM4 8
#define RESET_PWM5 9
#define RESET_PWM6 10
#define RESET_PWM7 11
#define RESET_PWM8 12
#define RESET_PWM9 13
#define RESET_PWM10 14
#define RESET_PWM11 15
#define RESET_PWM12 16
#define RESET_PWM13 17
#define RESET_PWM14 18
#define RESET_PWM15 19
#define RESET_PWM16 20
#define RESET_PWM17 21
#define RESET_PWM18 22
#define RESET_PWM19 23
#define RESET_SSP3 24
#define RESET_UART3 25
#define RESET_RTC 26
#define RESET_TWSI0 27
#define RESET_TIMERS1 28
#define RESET_AIB 29
#define RESET_TIMERS2 30
#define RESET_ONEWIRE 31
#define RESET_SSPA0 32
#define RESET_SSPA1 33
#define RESET_DRO 34
#define RESET_IR 35
#define RESET_TWSI1 36
#define RESET_TSEN 37
#define RESET_TWSI2 38
#define RESET_TWSI4 39
#define RESET_TWSI5 40
#define RESET_TWSI6 41
#define RESET_TWSI7 42
#define RESET_TWSI8 43
#define RESET_IPC_AP2AUD 44
#define RESET_UART4 45
#define RESET_UART5 46
#define RESET_UART6 47
#define RESET_UART7 48
#define RESET_UART8 49
#define RESET_UART9 50
#define RESET_CAN0 51
/* MPMU */
#define RESET_WDT 52
/* APMU */
#define RESET_JPG 53
#define RESET_CSI 54
#define RESET_CCIC2_PHY 55
#define RESET_CCIC3_PHY 56
#define RESET_ISP 57
#define RESET_ISP_AHB 58
#define RESET_ISP_CI 59
#define RESET_ISP_CPP 60
#define RESET_LCD 61
#define RESET_DSI_ESC 62
#define RESET_V2D 63
#define RESET_MIPI 64
#define RESET_LCD_SPI 65
#define RESET_LCD_SPI_BUS 66
#define RESET_LCD_SPI_HBUS 67
#define RESET_LCD_MCLK 68
#define RESET_CCIC_4X 69
#define RESET_CCIC1_PHY 70
#define RESET_SDH_AXI 71
#define RESET_SDH0 72
#define RESET_SDH1 73
#define RESET_USB_AXI 74
#define RESET_USBP1_AXI 75
#define RESET_USB3_0 76
#define RESET_QSPI 77
#define RESET_QSPI_BUS 78
#define RESET_DMA 79
#define RESET_AES 80
#define RESET_VPU 81
#define RESET_GPU 82
#define RESET_SDH2 83
#define RESET_MC 84
#define RESET_EM_AXI 85
#define RESET_EM 86
#define RESET_AUDIO_SYS 87
#define RESET_HDMI 88
#define RESET_PCIE0 89
#define RESET_PCIE1 90
#define RESET_PCIE2 91
#define RESET_EMAC0 92
#define RESET_EMAC1 93
/* APBC2 */
#define RESET_SEC_UART1 94
#define RESET_SEC_SSP2 95
#define RESET_SEC_TWSI3 96
#define RESET_SEC_RTC 97
#define RESET_SEC_TIMERS0 98
#define RESET_SEC_KPC 99
#define RESET_SEC_GPIO 100
#define RESET_NUMBER 101
#endif
+163
View File
@@ -0,0 +1,163 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2025-2026 RISCstar Ltd.
*/
#ifndef __SPACEMIT_P1_H_
#define __SPACEMIT_P1_H_
#define P1_MAX_REGS 0xA8
#define P1_REG_ID 0x0
#define P1_ID 0x2
#define P1_REG_BUCK1_CTRL 0x47
#define P1_REG_BUCK2_CTRL 0x4a
#define P1_REG_BUCK3_CTRL 0x4d
#define P1_REG_BUCK4_CTRL 0x50
#define P1_REG_BUCK5_CTRL 0x53
#define P1_REG_BUCK6_CTRL 0x56
#define P1_REG_BUCK1_VSEL 0x48
#define P1_REG_BUCK2_VSEL 0x4b
#define P1_REG_BUCK3_VSEL 0x4e
#define P1_REG_BUCK4_VSEL 0x51
#define P1_REG_BUCK5_VSEL 0x54
#define P1_REG_BUCK6_VSEL 0x57
#define P1_REG_BUCK1_SVSEL 0x49
#define P1_REG_BUCK2_SVSEL 0x4c
#define P1_REG_BUCK3_SVSEL 0x4f
#define P1_REG_BUCK4_SVSEL 0x52
#define P1_REG_BUCK5_SVSEL 0x55
#define P1_REG_BUCK6_SVSEL 0x58
#define P1_BUCK_CTRL(x) (0x47 + ((x) - 1) * 3)
#define P1_BUCK_VSEL(x) (0x48 + ((x) - 1) * 3)
#define P1_BUCK_SVSEL(x) (0x49 + ((x) - 1) * 3)
#define BUCK_VSEL_MASK 0xff
#define BUCK_EN_MASK 0x1
#define BUCK_SVSEL_MASK 0xff
#define P1_REG_ALDO1_CTRL 0x5b
#define P1_REG_ALDO2_CTRL 0x5e
#define P1_REG_ALDO3_CTRL 0x61
#define P1_REG_ALDO4_CTRL 0x64
#define P1_REG_ALDO1_VOLT 0x5c
#define P1_REG_ALDO2_VOLT 0x5f
#define P1_REG_ALDO3_VOLT 0x62
#define P1_REG_ALDO4_VOLT 0x65
#define P1_REG_ALDO1_SVOLT 0x5d
#define P1_REG_ALDO2_SVOLT 0x60
#define P1_REG_ALDO3_SVOLT 0x63
#define P1_REG_ALDO4_SVOLT 0x66
#define P1_ALDO_CTRL(x) (0x5b + ((x) - 1) * 3)
#define P1_ALDO_VOLT(x) (0x5c + ((x) - 1) * 3)
#define P1_ALDO_SVOLT(x) (0x5d + ((x) - 1) * 3)
#define ALDO_SVSEL_MASK 0x7f
#define ALDO_EN_MASK 0x1
#define ALDO_VSEL_MASK 0x7f
#define P1_REG_DLDO1_CTRL 0x67
#define P1_REG_DLDO2_CTRL 0x6a
#define P1_REG_DLDO3_CTRL 0x6d
#define P1_REG_DLDO4_CTRL 0x70
#define P1_REG_DLDO5_CTRL 0x73
#define P1_REG_DLDO6_CTRL 0x76
#define P1_REG_DLDO7_CTRL 0x79
#define P1_REG_DLDO1_VOLT 0x68
#define P1_REG_DLDO2_VOLT 0x6b
#define P1_REG_DLDO3_VOLT 0x6e
#define P1_REG_DLDO4_VOLT 0x71
#define P1_REG_DLDO5_VOLT 0x74
#define P1_REG_DLDO6_VOLT 0x77
#define P1_REG_DLDO7_VOLT 0x7a
#define P1_REG_DLDO1_SVOLT 0x69
#define P1_REG_DLDO2_SVOLT 0x6c
#define P1_REG_DLDO3_SVOLT 0x6f
#define P1_REG_DLDO4_SVOLT 0x72
#define P1_REG_DLDO5_SVOLT 0x75
#define P1_REG_DLDO6_SVOLT 0x78
#define P1_REG_DLDO7_SVOLT 0x7b
#define P1_DLDO_CTRL(x) (0x67 + ((x) - 1) * 3)
#define P1_DLDO_VOLT(x) (0x68 + ((x) - 1) * 3)
#define P1_DLDO_SVOLT(x) (0x69 + ((x) - 1) * 3)
#define DLDO_SVSEL_MASK 0x7f
#define DLDO_EN_MASK 0x1
#define DLDO_VSEL_MASK 0x7f
#define P1_REG_SWITCH_CTRL 0x59
#define P1_SWTICH_EN_MASK 0x1
#define P1_REG_SWITCH_PWRKEY_EVENT_CTRL 0x97
#define P1_SWITCH_PWRKEY_EVENT_EN_MSK 0xf
#define P1_REG_SWITCH_PWRKEY_INIT_CTRL 0x9e
#define P1_SWITCH_PWRKEY_INT_EN_MSK 0xf
/* Watchdog Timer Registers */
#define P1_WDT_CTRL 0x44
#define P1_PWR_CTRL0 0x7C
#define P1_PWR_CTRL2 0x7E
#define P1_PWR_CTRL2_MSK 0xff
/* Watchdog Timer Control Bits */
#define P1_WDT_CLEAR_STATUS 0x1
#define P1_SW_RST 0x2
#define P1_WDT_RESET_ENABLE 0x80
#define P1_WDT_ENABLE 0x8
#define P1_WDT_TIMEOUT_1S 0x0
#define P1_WDT_TIMEOUT_4S 0x1
#define P1_WDT_TIMEOUT_8S 0x2
#define P1_WDT_TIMEOUT_16S 0x3
#define P1_RTC_TICK_CTRL 0x1d
#define P1_RTC_TICK_CTRL_MSK 0x7f
#define P1_RTC_TICK_EVENT 0x92
#define P1_RTC_TICK_EVENT_MSK 0x3f
#define P1_RTC_TICK_IRQ 0x99
#define P1_RTC_TICK_IRQ_MSK 0x3f
#define P1_REG_ALIVE 0xab
#define P1_ALIVE_MSK 0x7
#define SYS_REBOOT_FLAG_BIT 0x2
/* SWITCH ID */
enum {
P1_ID_SWITCH1,
P1_ID_SWITCH1_PWRKEY_EVENT,
P1_ID_SWITCH1_PWRKEY_INT,
P1_ID_SWITCH_RTC_TICK_CTRL,
P1_ID_SWITCH_RTC_TICK_EVENT,
P1_ID_SWITCH_RTC_TCK_IRQ,
P1_ID_SWITCH_POWER_DOWN,
P1_ID_SWITCH_CHARGING_FLAG,
};
/* POWERKEY events */
enum {
PWRKEY_RISING_EVENT = 1,
PWRKEY_FAILING_EVENT = 2,
PWRKEY_SHORT_PRESS_EVENT = 4,
PWRKEY_LONG_PRESS_EVENT = 8,
};
#define P1_BUCK_DRIVER "p1_buck"
#define P1_ALDO_DRIVER "p1_aldo"
#define P1_DLDO_DRIVER "p1_dldo"
#define P1_SWITCH_DRIVER "p1_switch"
#define P1_WDT_DRIVER "p1_wdt"
#endif /* __SPACEMIT_P1_H_ */
+23
View File
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* SpacemiT K1 reset driver — header interface.
*
* Copyright (C) 2026 RISCstar Ltd.
*/
#ifndef _SOC_SPACEMIT_K1_RESET_H
#define _SOC_SPACEMIT_K1_RESET_H
struct udevice;
enum spacemit_k1_reset_syscon {
SPACEMIT_K1_RESET_MPMU,
SPACEMIT_K1_RESET_APMU,
SPACEMIT_K1_RESET_APBC,
SPACEMIT_K1_RESET_APBC2,
};
int spacemit_k1_reset_bind(struct udevice *parent,
enum spacemit_k1_reset_syscon syscon);
#endif /* _SOC_SPACEMIT_K1_RESET_H */
+149
View File
@@ -0,0 +1,149 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* SpacemiT clock and reset driver definitions for the K1 SoC */
#ifndef __SOC_K1_SYSCON_H__
#define __SOC_K1_SYSCON_H__
/* APBS register offset */
#define APBS_PLL1_SWCR1 0x100
#define APBS_PLL1_SWCR2 0x104
#define APBS_PLL1_SWCR3 0x108
#define APBS_PLL2_SWCR1 0x118
#define APBS_PLL2_SWCR2 0x11c
#define APBS_PLL2_SWCR3 0x120
#define APBS_PLL3_SWCR1 0x124
#define APBS_PLL3_SWCR2 0x128
#define APBS_PLL3_SWCR3 0x12c
/* MPMU register offset */
#define MPMU_POSR 0x0010
#define MPMU_FCCR 0x0008
#define POSR_PLL1_LOCK BIT(27)
#define POSR_PLL2_LOCK BIT(28)
#define POSR_PLL3_LOCK BIT(29)
#define MPMU_SUCCR 0x0014
#define MPMU_ISCCR 0x0044
#define MPMU_WDTPCR 0x0200
#define MPMU_RIPCCR 0x0210
#define MPMU_ACGR 0x1024
#define MPMU_APBCSCR 0x1050
#define MPMU_SUCCR_1 0x10b0
/* APBC register offset */
#define APBC_UART1_CLK_RST 0x00
#define APBC_UART2_CLK_RST 0x04
#define APBC_GPIO_CLK_RST 0x08
#define APBC_PWM0_CLK_RST 0x0c
#define APBC_PWM1_CLK_RST 0x10
#define APBC_PWM2_CLK_RST 0x14
#define APBC_PWM3_CLK_RST 0x18
#define APBC_TWSI8_CLK_RST 0x20
#define APBC_UART3_CLK_RST 0x24
#define APBC_RTC_CLK_RST 0x28
#define APBC_TWSI0_CLK_RST 0x2c
#define APBC_TWSI1_CLK_RST 0x30
#define APBC_TIMERS1_CLK_RST 0x34
#define APBC_TWSI2_CLK_RST 0x38
#define APBC_AIB_CLK_RST 0x3c
#define APBC_TWSI4_CLK_RST 0x40
#define APBC_TIMERS2_CLK_RST 0x44
#define APBC_ONEWIRE_CLK_RST 0x48
#define APBC_TWSI5_CLK_RST 0x4c
#define APBC_DRO_CLK_RST 0x58
#define APBC_IR_CLK_RST 0x5c
#define APBC_TWSI6_CLK_RST 0x60
#define APBC_COUNTER_CLK_SEL 0x64
#define APBC_TWSI7_CLK_RST 0x68
#define APBC_TSEN_CLK_RST 0x6c
#define APBC_UART4_CLK_RST 0x70
#define APBC_UART5_CLK_RST 0x74
#define APBC_UART6_CLK_RST 0x78
#define APBC_SSP3_CLK_RST 0x7c
#define APBC_SSPA0_CLK_RST 0x80
#define APBC_SSPA1_CLK_RST 0x84
#define APBC_IPC_AP2AUD_CLK_RST 0x90
#define APBC_UART7_CLK_RST 0x94
#define APBC_UART8_CLK_RST 0x98
#define APBC_UART9_CLK_RST 0x9c
#define APBC_CAN0_CLK_RST 0xa0
#define APBC_PWM4_CLK_RST 0xa8
#define APBC_PWM5_CLK_RST 0xac
#define APBC_PWM6_CLK_RST 0xb0
#define APBC_PWM7_CLK_RST 0xb4
#define APBC_PWM8_CLK_RST 0xb8
#define APBC_PWM9_CLK_RST 0xbc
#define APBC_PWM10_CLK_RST 0xc0
#define APBC_PWM11_CLK_RST 0xc4
#define APBC_PWM12_CLK_RST 0xc8
#define APBC_PWM13_CLK_RST 0xcc
#define APBC_PWM14_CLK_RST 0xd0
#define APBC_PWM15_CLK_RST 0xd4
#define APBC_PWM16_CLK_RST 0xd8
#define APBC_PWM17_CLK_RST 0xdc
#define APBC_PWM18_CLK_RST 0xe0
#define APBC_PWM19_CLK_RST 0xe4
/* APMU register offset */
#define APMU_JPG_CLK_RES_CTRL 0x020
#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x024
#define APMU_ISP_CLK_RES_CTRL 0x038
#define APMU_LCD_CLK_RES_CTRL1 0x044
#define APMU_LCD_SPI_CLK_RES_CTRL 0x048
#define APMU_LCD_CLK_RES_CTRL2 0x04c
#define APMU_CCIC_CLK_RES_CTRL 0x050
#define APMU_SDH0_CLK_RES_CTRL 0x054
#define APMU_SDH1_CLK_RES_CTRL 0x058
#define APMU_USB_CLK_RES_CTRL 0x05c
#define APMU_QSPI_CLK_RES_CTRL 0x060
#define APMU_DMA_CLK_RES_CTRL 0x064
#define APMU_AES_CLK_RES_CTRL 0x068
#define APMU_VPU_CLK_RES_CTRL 0x0a4
#define APMU_GPU_CLK_RES_CTRL 0x0cc
#define APMU_SDH2_CLK_RES_CTRL 0x0e0
#define APMU_PMUA_MC_CTRL 0x0e8
#define APMU_PMU_CC2_AP 0x100
#define APMU_PMUA_EM_CLK_RES_CTRL 0x104
#define APMU_AUDIO_CLK_RES_CTRL 0x14c
#define APMU_HDMI_CLK_RES_CTRL 0x1b8
#define APMU_CCI550_CLK_CTRL 0x300
#define APMU_ACLK_CLK_CTRL 0x388
#define APMU_CPU_C0_CLK_CTRL 0x38C
#define APMU_CPU_C1_CLK_CTRL 0x390
#define APMU_PCIE_CLK_RES_CTRL_0 0x3cc
#define APMU_PCIE_CLK_RES_CTRL_1 0x3d4
#define APMU_PCIE_CLK_RES_CTRL_2 0x3dc
#define APMU_EMAC0_CLK_RES_CTRL 0x3e4
#define APMU_EMAC1_CLK_RES_CTRL 0x3ec
/* RCPU register offsets */
#define RCPU_SSP0_CLK_RST 0x0028
#define RCPU_I2C0_CLK_RST 0x0030
#define RCPU_UART1_CLK_RST 0x003c
#define RCPU_CAN_CLK_RST 0x0048
#define RCPU_IR_CLK_RST 0x004c
#define RCPU_UART0_CLK_RST 0x00d8
#define AUDIO_HDMI_CLK_CTRL 0x2044
/* RCPU2 register offsets */
#define RCPU2_PWM0_CLK_RST 0x0000
#define RCPU2_PWM1_CLK_RST 0x0004
#define RCPU2_PWM2_CLK_RST 0x0008
#define RCPU2_PWM3_CLK_RST 0x000c
#define RCPU2_PWM4_CLK_RST 0x0010
#define RCPU2_PWM5_CLK_RST 0x0014
#define RCPU2_PWM6_CLK_RST 0x0018
#define RCPU2_PWM7_CLK_RST 0x001c
#define RCPU2_PWM8_CLK_RST 0x0020
#define RCPU2_PWM9_CLK_RST 0x0024
/* APBC2 register offsets */
#define APBC2_UART1_CLK_RST 0x0000
#define APBC2_SSP2_CLK_RST 0x0004
#define APBC2_TWSI3_CLK_RST 0x0008
#define APBC2_RTC_CLK_RST 0x000c
#define APBC2_TIMERS0_CLK_RST 0x0010
#define APBC2_KPC_CLK_RST 0x0014
#define APBC2_GPIO_CLK_RST 0x001c
#endif /* __SOC_K1_SYSCON_H__ */