Merge tag 'riscv-for-v2026.10-rc1' of https://git.u-boot-project.org/u-boot/custodians/u-boot-riscv
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:
+2
-2
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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";
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -6,4 +6,5 @@ SpacemiT
|
||||
:maxdepth: 1
|
||||
|
||||
bananapi-f3
|
||||
k1-spl
|
||||
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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_ */
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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_ */
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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_ */
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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_ */
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-y += pinctrl-k1.o
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# SpacemiT reset drivers
|
||||
|
||||
obj-y += reset-spacemit-k1.o
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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, },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -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 */
|
||||
@@ -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__ */
|
||||
Reference in New Issue
Block a user