diff options
| author | Tom Rini <[email protected]> | 2026-07-21 09:06:07 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-07-21 09:06:07 -0600 |
| commit | f5f06b10e190ebd84fe24c2b4cc1c294c596f8f8 (patch) | |
| tree | 13e0a586d97757e2ba294b375a9a42487c8f97e6 | |
| parent | 5a83cae32d9a1f29209d4f171a8de18bf82fd630 (diff) | |
| parent | 0926356edd68e180bbb214f0bb277037d9415129 (diff) | |
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.
67 files changed, 6864 insertions, 1270 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 549a703e72f..323b1b172b1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 <[email protected]> -M: Leo <[email protected]> +M: Tim Ouyang <[email protected]> +M: Leo Liang <[email protected]> S: Maintained T: git https://git.u-boot-project.org/u-boot/custodians/u-boot-riscv.git F: arch/riscv/ diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index ad7589123c6..485067ed266 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -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" diff --git a/arch/riscv/cpu/k1/Kconfig b/arch/riscv/cpu/k1/Kconfig index 14201df80f2..b155535658d 100644 --- a/arch/riscv/cpu/k1/Kconfig +++ b/arch/riscv/cpu/k1/Kconfig @@ -1,12 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-or-later # # Copyright (C) 2024, Kongyang Liu <[email protected]> +# 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 diff --git a/arch/riscv/dts/Makefile b/arch/riscv/dts/Makefile index 8e591cb7aa9..71c5c13d221 100644 --- a/arch/riscv/dts/Makefile +++ b/arch/riscv/dts/Makefile @@ -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 diff --git a/arch/riscv/dts/binman.dtsi b/arch/riscv/dts/binman.dtsi index b518560bb94..84c261c6849 100644 --- a/arch/riscv/dts/binman.dtsi +++ b/arch/riscv/dts/binman.dtsi @@ -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 { diff --git a/arch/riscv/dts/k1-bananapi-f3-u-boot.dtsi b/arch/riscv/dts/k1-bananapi-f3-u-boot.dtsi new file mode 100644 index 00000000000..7f9443d6951 --- /dev/null +++ b/arch/riscv/dts/k1-bananapi-f3-u-boot.dtsi @@ -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>; + }; + }; +}; diff --git a/arch/riscv/dts/k1-bananapi-f3.dts b/arch/riscv/dts/k1-bananapi-f3.dts deleted file mode 100644 index 6b5b83bcdb9..00000000000 --- a/arch/riscv/dts/k1-bananapi-f3.dts +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* - * Copyright (C) 2024 Yangyu Chen <[email protected]> - */ - -#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"; -}; diff --git a/arch/riscv/dts/k1-musepi-pro-u-boot.dtsi b/arch/riscv/dts/k1-musepi-pro-u-boot.dtsi new file mode 100644 index 00000000000..8a9a2a09de9 --- /dev/null +++ b/arch/riscv/dts/k1-musepi-pro-u-boot.dtsi @@ -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>; + }; + }; +}; diff --git a/arch/riscv/dts/k1-pinctrl.dtsi b/arch/riscv/dts/k1-pinctrl.dtsi deleted file mode 100644 index 14e7096fbcf..00000000000 --- a/arch/riscv/dts/k1-pinctrl.dtsi +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR MIT) -/* - * Copyright (C) 2022 Spacemit Inc. - * Copyright (C) 2025 Yixun Lan <[email protected]> - */ - -#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>; - }; - }; -}; diff --git a/arch/riscv/dts/k1.dtsi b/arch/riscv/dts/k1.dtsi deleted file mode 100644 index a633e43da32..00000000000 --- a/arch/riscv/dts/k1.dtsi +++ /dev/null @@ -1,480 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* - * Copyright (C) 2024 Yangyu Chen <[email protected]> - */ - -/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>; - }; - }; -}; diff --git a/board/spacemit/bananapi-f3/MAINTAINERS b/board/spacemit/bananapi-f3/MAINTAINERS deleted file mode 100644 index 131bad03181..00000000000 --- a/board/spacemit/bananapi-f3/MAINTAINERS +++ /dev/null @@ -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 diff --git a/board/spacemit/bananapi-f3/Makefile b/board/spacemit/bananapi-f3/Makefile deleted file mode 100644 index 2168698402b..00000000000 --- a/board/spacemit/bananapi-f3/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# -# Copyright (c) 2024, Kongyang Liu <[email protected]> - -obj-y := board.o diff --git a/board/spacemit/bananapi-f3/Kconfig b/board/spacemit/k1/Kconfig index f89fa9af2c7..1c615f89b64 100644 --- a/board/spacemit/bananapi-f3/Kconfig +++ b/board/spacemit/k1/Kconfig @@ -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 diff --git a/board/spacemit/k1/MAINTAINERS b/board/spacemit/k1/MAINTAINERS new file mode 100644 index 00000000000..32d47ecc8f1 --- /dev/null +++ b/board/spacemit/k1/MAINTAINERS @@ -0,0 +1,14 @@ +BananaPi F3 +M: Huan Zhou <[email protected]> +M: Guodong Xu <[email protected]> +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 diff --git a/board/spacemit/k1/Makefile b/board/spacemit/k1/Makefile new file mode 100644 index 00000000000..a57af2e1f08 --- /dev/null +++ b/board/spacemit/k1/Makefile @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2024, Kongyang Liu <[email protected]> +# 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) diff --git a/board/spacemit/bananapi-f3/board.c b/board/spacemit/k1/board.c index ea416621544..ea416621544 100644 --- a/board/spacemit/bananapi-f3/board.c +++ b/board/spacemit/k1/board.c diff --git a/board/spacemit/k1/spl.c b/board/spacemit/k1/spl.c new file mode 100644 index 00000000000..d749e21a2d5 --- /dev/null +++ b/board/spacemit/k1/spl.c @@ -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; +} diff --git a/board/spacemit/k1/tlv_codes.h b/board/spacemit/k1/tlv_codes.h new file mode 100644 index 00000000000..e40fbdcee6d --- /dev/null +++ b/board/spacemit/k1/tlv_codes.h @@ -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 */ diff --git a/configs/bananapi-f3_defconfig b/configs/bananapi-f3_defconfig deleted file mode 100644 index a726ce84775..00000000000 --- a/configs/bananapi-f3_defconfig +++ /dev/null @@ -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 diff --git a/configs/spacemit_k1_defconfig b/configs/spacemit_k1_defconfig new file mode 100644 index 00000000000..0604c2feefc --- /dev/null +++ b/configs/spacemit_k1_defconfig @@ -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 diff --git a/doc/board/spacemit/bananapi-f3.rst b/doc/board/spacemit/bananapi-f3.rst index f2220950a3a..1ece2ce9d02 100644 --- a/doc/board/spacemit/bananapi-f3.rst +++ b/doc/board/spacemit/bananapi-f3.rst @@ -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 diff --git a/doc/board/spacemit/index.rst b/doc/board/spacemit/index.rst index e7d3d94e459..a5e35ee12ab 100644 --- a/doc/board/spacemit/index.rst +++ b/doc/board/spacemit/index.rst @@ -6,4 +6,5 @@ SpacemiT :maxdepth: 1 bananapi-f3 + k1-spl diff --git a/doc/board/spacemit/k1-spl.rst b/doc/board/spacemit/k1-spl.rst new file mode 100644 index 00000000000..187eed56e8e --- /dev/null +++ b/doc/board/spacemit/k1-spl.rst @@ -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>`_ diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index e69b0543817..145e4cfa2de 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -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" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index c37ef75d420..2bfa789e48a 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -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 diff --git a/drivers/clk/spacemit/Kconfig b/drivers/clk/spacemit/Kconfig new file mode 100644 index 00000000000..25a3c1e4a45 --- /dev/null +++ b/drivers/clk/spacemit/Kconfig @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2025, Junhui Liu <[email protected]> + +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 diff --git a/drivers/clk/spacemit/Makefile b/drivers/clk/spacemit/Makefile new file mode 100644 index 00000000000..824e94d1f74 --- /dev/null +++ b/drivers/clk/spacemit/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2025 Junhui Liu <[email protected]> + +obj-$(CONFIG_CLK_SPACEMIT) += clk_ddn.o clk_mix.o clk_pll.o + +obj-$(CONFIG_CLK_SPACEMIT_K1) += clk-k1.o diff --git a/drivers/clk/spacemit/clk-k1.c b/drivers/clk/spacemit/clk-k1.c new file mode 100644 index 00000000000..07adc126ee3 --- /dev/null +++ b/drivers/clk/spacemit/clk-k1.c @@ -0,0 +1,1878 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 SpacemiT Technology Co. Ltd + * Copyright (c) 2024-2025 Haylen Chu <[email protected]> + * Copyright (c) 2025 Junhui Liu <[email protected]> + * Copyright (c) 2025-2026 RISCstar Ltd. + * + * Authors: Haylen Chu <[email protected]> + */ + +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/lists.h> +#include <regmap.h> +#include <linux/clk-provider.h> +#include <soc/spacemit/k1-reset.h> +#include <soc/spacemit/k1-syscon.h> + +#include "clk_common.h" +#include "clk_ddn.h" +#include "clk_mix.h" +#include "clk_pll.h" + +#include <dt-bindings/clock/spacemit,k1-syscon.h> + +#define K1_PLL_ID 100 +#define K1_MPMU_ID 200 +#define K1_APBC_ID 300 +#define K1_APMU_ID 400 + +struct spacemit_ccu_data { + struct clk **clks; + size_t num; + unsigned long offset; +}; + +/* APBS clocks start, APBS region contains and only contains all PLL clocks */ + +/* + * PLL{1,2} must run at fixed frequencies to provide clocks in correct rates for + * peripherals. + */ +static const struct ccu_pll_rate_tbl pll1_rate_tbl[] = { + CCU_PLL_RATE(2457600000UL, 0x0050dd64, 0x330ccccd), +}; + +#if IS_ENABLED(CONFIG_SPL_BUILD) +CCU_PLL_DEFINE(CLK_PLL1, pll1, pll1, "clock-24m", pll1_rate_tbl, + APBS_PLL1_SWCR1, APBS_PLL1_SWCR3, MPMU_POSR, POSR_PLL1_LOCK, + CLK_SET_RATE_GATE); + +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D4, pll1_d4, pll1_d4, "pll1", APBS_PLL1_SWCR2, + BIT(3), 4, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D6, pll1_d6, pll1_d6, "pll1", APBS_PLL1_SWCR2, + BIT(5), 6, 1); +CCU_FACTOR_GATE_FLAGS_DEFINE(CLK_PLL1_D8, pll1_d8, pll1_d8, "pll1", + APBS_PLL1_SWCR2, BIT(7), 8, 1, CLK_IS_CRITICAL); +#else +static const struct ccu_pll_rate_tbl pll2_rate_tbl[] = { + CCU_PLL_RATE(3000000000UL, 0x0050dd66, 0x3fe00000), +}; + +static const struct ccu_pll_rate_tbl pll3_rate_tbl[] = { + CCU_PLL_RATE(1600000000UL, 0x0050cd61, 0x43eaaaab), + CCU_PLL_RATE(1800000000UL, 0x0050cd61, 0x4b000000), + CCU_PLL_RATE(2000000000UL, 0x0050dd62, 0x2aeaaaab), + CCU_PLL_RATE(2457600000UL, 0x0050dd64, 0x330ccccd), + CCU_PLL_RATE(3000000000UL, 0x0050dd66, 0x3fe00000), + CCU_PLL_RATE(3200000000UL, 0x0050dd67, 0x43eaaaab), +}; + +CCU_PLL_DEFINE(CLK_PLL1, pll1, pll1, "clock-24m", pll1_rate_tbl, + APBS_PLL1_SWCR1, APBS_PLL1_SWCR3, MPMU_POSR, POSR_PLL1_LOCK, + CLK_SET_RATE_GATE); +CCU_PLL_DEFINE(CLK_PLL2, pll2, pll2, "clock-24m", pll2_rate_tbl, + APBS_PLL2_SWCR1, APBS_PLL2_SWCR3, MPMU_POSR, POSR_PLL2_LOCK, + CLK_SET_RATE_GATE); +CCU_PLL_DEFINE(CLK_PLL3, pll3, pll3, "clock-24m", pll3_rate_tbl, + APBS_PLL3_SWCR1, APBS_PLL3_SWCR3, MPMU_POSR, POSR_PLL3_LOCK, + CLK_SET_RATE_GATE); + +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D2, pll1_d2, pll1_d2, "pll1", APBS_PLL1_SWCR2, + BIT(1), 2, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D3, pll1_d3, pll1_d3, "pll1", APBS_PLL1_SWCR2, + BIT(2), 3, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D4, pll1_d4, pll1_d4, "pll1", APBS_PLL1_SWCR2, + BIT(3), 4, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D5, pll1_d5, pll1_d5, "pll1", APBS_PLL1_SWCR2, + BIT(4), 5, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D6, pll1_d6, pll1_d6, "pll1", APBS_PLL1_SWCR2, + BIT(5), 6, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D7, pll1_d7, pll1_d7, "pll1", APBS_PLL1_SWCR2, + BIT(6), 7, 1); +CCU_FACTOR_GATE_FLAGS_DEFINE(CLK_PLL1_D8, pll1_d8, pll1_d8, "pll1", + APBS_PLL1_SWCR2, BIT(7), 8, 1, CLK_IS_CRITICAL); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D11, pll1_d11_223p4, pll1_d11_223p4, "pll1", + APBS_PLL1_SWCR2, BIT(15), 11, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D13, pll1_d13_189, pll1_d13_189, "pll1", + APBS_PLL1_SWCR2, BIT(16), 13, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D23, pll1_d23_106p8, pll1_d23_106p8, "pll1", + APBS_PLL1_SWCR2, BIT(20), 23, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D64, pll1_d64_38p4, pll1_d64_38p4, "pll1", + APBS_PLL1_SWCR2, BIT(0), 64, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D10_AUD, pll1_aud_245p7, pll1_aud_245p7, "pll1", + APBS_PLL1_SWCR2, BIT(10), 10, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D100_AUD, pll1_aud_24p5, pll1_aud_24p5, "pll1", + APBS_PLL1_SWCR2, BIT(11), 100, 1); + +CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D1, pll2_d1, pll2_d1, "pll2", APBS_PLL2_SWCR2, + BIT(0), 1, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D2, pll2_d2, pll2_d2, "pll2", APBS_PLL2_SWCR2, + BIT(1), 2, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D3, pll2_d3, pll2_d3, "pll2", APBS_PLL2_SWCR2, + BIT(2), 3, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D4, pll2_d4, pll2_d4, "pll2", APBS_PLL2_SWCR2, + BIT(3), 4, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D5, pll2_d5, pll2_d5, "pll2", APBS_PLL2_SWCR2, + BIT(4), 5, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D6, pll2_d6, pll2_d6, "pll2", APBS_PLL2_SWCR2, + BIT(5), 6, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D7, pll2_d7, pll2_d7, "pll2", APBS_PLL2_SWCR2, + BIT(6), 7, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D8, pll2_d8, pll2_d8, "pll2", APBS_PLL2_SWCR2, + BIT(7), 8, 1); + +CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D1, pll3_d1, pll3_d1, "pll3", APBS_PLL3_SWCR2, + BIT(0), 1, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D2, pll3_d2, pll3_d2, "pll3", APBS_PLL3_SWCR2, + BIT(1), 2, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D3, pll3_d3, pll3_d3, "pll3", APBS_PLL3_SWCR2, + BIT(2), 3, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D4, pll3_d4, pll3_d4, "pll3", APBS_PLL3_SWCR2, + BIT(3), 4, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D5, pll3_d5, pll3_d5, "pll3", APBS_PLL3_SWCR2, + BIT(4), 5, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D6, pll3_d6, pll3_d6, "pll3", APBS_PLL3_SWCR2, + BIT(5), 6, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D7, pll3_d7, pll3_d7, "pll3", APBS_PLL3_SWCR2, + BIT(6), 7, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D8, pll3_d8, pll3_d8, "pll3", APBS_PLL3_SWCR2, + BIT(7), 8, 1); + +CCU_FACTOR_DEFINE(CLK_PLL3_20, pll3_20, pll3_20, "pll3_d8", 20, 1); +CCU_FACTOR_DEFINE(CLK_PLL3_40, pll3_40, pll3_40, "pll3_d8", 10, 1); +CCU_FACTOR_DEFINE(CLK_PLL3_80, pll3_80, pll3_80, "pll3_d8", 5, 1); +#endif +/* APBS clocks end */ + +/* MPMU clocks start */ +#if IS_ENABLED(CONFIG_SPL_BUILD) +CCU_GATE_DEFINE(CLK_PLL1_614, pll1_d4_614p4, pll1_d4_614p4, "pll1_d4", + MPMU_ACGR, BIT(15), 0); +CCU_GATE_DEFINE(CLK_PLL1_409P6, pll1_d6_409p6, pll1_d6_409p6, "pll1_d6", + MPMU_ACGR, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PLL1_307P2, pll1_d8_307p2, pll1_d8_307p2, "pll1_d8", + MPMU_ACGR, BIT(13), 0); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_31P5, pll1_d78_31p5, pll1_d78_31p5, + "pll1_d4", MPMU_ACGR, BIT(6), 39, 2); +CCU_DDN_DEFINE(CLK_SLOW_UART2, slow_uart2_48, slow_uart2_48, + "pll1_d4_614p4", MPMU_SUCCR_1, + CCU_DDN_MASK(16, 13), 16, CCU_DDN_MASK(0, 13), 0, 2, 0); +#else +CCU_GATE_DEFINE(CLK_PLL1_307P2, pll1_d8_307p2, pll1_d8_307p2, "pll1_d8", + MPMU_ACGR, BIT(13), 0); + +CCU_FACTOR_DEFINE(CLK_PLL1_76P8, pll1_d32_76p8, pll1_d32_76p8, "pll1_d8_307p2", + 4, 1); + +CCU_FACTOR_DEFINE(CLK_PLL1_61P44, pll1_d40_61p44, pll1_d40_61p44, + "pll1_d8_307p2", 5, 1); + +CCU_FACTOR_DEFINE(CLK_PLL1_153P6, pll1_d16_153p6, pll1_d16_153p6, + "pll1_d8", 2, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_102P4, pll1_d24_102p4, pll1_d24_102p4, + "pll1_d8", MPMU_ACGR, BIT(12), 3, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_51P2, pll1_d48_51p2, pll1_d48_51p2, + "pll1_d8", MPMU_ACGR, BIT(7), 6, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_51P2_AP, pll1_d48_51p2_ap, pll1_d48_51p2_ap, + "pll1_d8", MPMU_ACGR, BIT(11), 6, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_57P6, pll1_m3d128_57p6, pll1_m3d128_57p6, + "pll1_d8", MPMU_ACGR, BIT(8), 16, 3); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_25P6, pll1_d96_25p6, pll1_d96_25p6, + "pll1_d8", MPMU_ACGR, BIT(4), 12, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_12P8, pll1_d192_12p8, pll1_d192_12p8, + "pll1_d8", MPMU_ACGR, BIT(3), 24, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_12P8_WDT, pll1_d192_12p8_wdt, pll1_d192_12p8_wdt, + "pll1_d8", MPMU_ACGR, BIT(19), 24, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_6P4, pll1_d384_6p4, pll1_d384_6p4, + "pll1_d8", MPMU_ACGR, BIT(2), 48, 1); + +CCU_FACTOR_DEFINE(CLK_PLL1_3P2, pll1_d768_3p2, pll1_d768_3p2, + "pll1_d384_6p4", 2, 1); +CCU_FACTOR_DEFINE(CLK_PLL1_1P6, pll1_d1536_1p6, pll1_d1536_1p6, + "pll1_d384_6p4", 4, 1); +CCU_FACTOR_DEFINE(CLK_PLL1_0P8, pll1_d3072_0p8, pll1_d3072_0p8, + "pll1_d384_6p4", 8, 1); + +CCU_GATE_DEFINE(CLK_PLL1_409P6, pll1_d6_409p6, pll1_d6_409p6, "pll1_d6", + MPMU_ACGR, BIT(0), 0); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_204P8, pll1_d12_204p8, pll1_d12_204p8, + "pll1_d6", MPMU_ACGR, BIT(5), 2, 1); + +CCU_GATE_DEFINE(CLK_PLL1_491, pll1_d5_491p52, pll1_d5_491p52, "pll1_d5", + MPMU_ACGR, BIT(21), 0); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_245P76, pll1_d10_245p76, pll1_d10_245p76, + "pll1_d5", MPMU_ACGR, BIT(18), 2, 1); + +CCU_GATE_DEFINE(CLK_PLL1_614, pll1_d4_614p4, pll1_d4_614p4, "pll1_d4", + MPMU_ACGR, BIT(15), 0); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_47P26, pll1_d52_47p26, pll1_d52_47p26, + "pll1_d4", MPMU_ACGR, BIT(10), 13, 1); +CCU_FACTOR_GATE_DEFINE(CLK_PLL1_31P5, pll1_d78_31p5, pll1_d78_31p5, + "pll1_d4", MPMU_ACGR, BIT(6), 39, 2); + +CCU_GATE_DEFINE(CLK_PLL1_819, pll1_d3_819p2, pll1_d3_819p2, "pll1_d3", + MPMU_ACGR, BIT(14), 0); + +CCU_GATE_DEFINE(CLK_PLL1_1228, pll1_d2_1228p8, pll1_d2_1228p8, "pll1_d2", + MPMU_ACGR, BIT(16), 0); + +CCU_GATE_DEFINE(CLK_SLOW_UART, slow_uart, slow_uart, "clock-32k", MPMU_ACGR, + BIT(1), CLK_IGNORE_UNUSED); +CCU_DDN_DEFINE(CLK_SLOW_UART1, slow_uart1_14p74, slow_uart1_14p74, + "pll1_d16_153p6", MPMU_SUCCR, + CCU_DDN_MASK(16, 13), 16, CCU_DDN_MASK(0, 13), 0, 2, 0); +CCU_DDN_DEFINE(CLK_SLOW_UART2, slow_uart2_48, slow_uart2_48, + "pll1_d4_614p4", MPMU_SUCCR_1, + CCU_DDN_MASK(16, 13), 16, CCU_DDN_MASK(0, 13), 0, 2, 0); + +CCU_GATE_DEFINE(CLK_WDT, wdt_clk, wdt_clk, "pll1_d96_25p6", MPMU_WDTPCR, + BIT(1), 0); + +CCU_FACTOR_DEFINE(CLK_I2S_153P6, i2s_153p6, i2s_153p6, "pll1_d8_307p2", 2, 1); + +static const char * const i2s_153p6_base_parents[] = { + "i2s_153p6", + "pll1_d8_307p2", +}; + +CCU_MUX_DEFINE(CLK_I2S_153P6_BASE, i2s_153p6_base, i2s_153p6_base, + i2s_153p6_base_parents, ARRAY_SIZE(i2s_153p6_base_parents), + MPMU_FCCR, 29, 1, 0); + +static const char * const i2s_sysclk_src_parents[] = { + "pll1_d96_25p6", + "i2s_153p6_base" +}; + +CCU_MUX_GATE_DEFINE(CLK_I2S_SYSCLK_SRC, i2s_sysclk_src, i2s_sysclk_src, + i2s_sysclk_src_parents, ARRAY_SIZE(i2s_sysclk_src_parents), + MPMU_ISCCR, 30, 1, BIT(31), 0); + +CCU_DDN_DEFINE(CLK_I2S_SYSCLK, i2s_sysclk, i2s_sysclk, "i2s_sysclk_src", + MPMU_ISCCR, CCU_DDN_MASK(0, 15), 0, CCU_DDN_MASK(15, 12), + 15, 1, 0); + +CCU_FACTOR_DEFINE(CLK_I2S_BCLK_FACTOR, i2s_bclk_factor, i2s_bclk_factor, + "i2s_sysclk", 2, 1); +/* + * Divider of i2s_bclk always implies a 1/2 factor, which is + * described by i2s_bclk_factor. + */ +CCU_DIV_GATE_DEFINE(CLK_I2S_BCLK, i2s_bclk, i2s_bclk, "i2s_bclk_factor", + MPMU_ISCCR, 27, 2, BIT(29), 0); + +static const char * const apb_parents[] = { + "pll1_d96_25p6", + "pll1_d48_51p2", + "pll1_d96_25p6", + "pll1_d24_102p4", +}; + +CCU_MUX_DEFINE(CLK_APB, apb_clk, apb_clk, apb_parents, ARRAY_SIZE(apb_parents), + MPMU_APBCSCR, 0, 2, 0); + +CCU_GATE_DEFINE(CLK_WDT_BUS, wdt_bus_clk, wdt_bus_clk, "apb_clk", MPMU_WDTPCR, + BIT(0), 0); + +CCU_GATE_DEFINE(CLK_RIPC, ripc_clk, ripc_clk, "apb_clk", MPMU_RIPCCR, 0x1, 0); +#endif +/* MPMU clocks end */ + +/* APBC clocks start */ +#if IS_ENABLED(CONFIG_SPL_BUILD) +static const char * const uart_clk_parents[] = { + "clock-dummy", + "clock-dummy", + "slow_uart2_48", +}; + +CCU_MUX_GATE_DEFINE(CLK_UART0, uart0_clk, uart0_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART1_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); + +static const char * const twsi_parents[] = { + "pll1_d78_31p5", +}; + +CCU_MUX_GATE_DEFINE(CLK_TWSI2, twsi2_clk, twsi2_clk, twsi_parents, + ARRAY_SIZE(twsi_parents), APBC_TWSI2_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +/* + * APBC_TWSI8_CLK_RST has a quirk that reading always results in zero. + * Combine functional and bus bits together as a gate to avoid sharing the + * write-only register between different clock hardwares. + */ +CCU_GATE_DEFINE(CLK_TWSI8, twsi8_clk, twsi8_clk, "pll1_d78_31p5", + APBC_TWSI8_CLK_RST, BIT(1) | BIT(0), 0); + +#else +static const char * const uart_clk_parents[] = { + "pll1_m3d128_57p6", + "slow_uart1_14p74", + "slow_uart2_48", +}; + +CCU_MUX_GATE_DEFINE(CLK_UART0, uart0_clk, uart0_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART1_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); + +static const char * const twsi_parents[] = { + "pll1_d78_31p5", + "pll1_d48_51p2", + "pll1_d40_61p44", +}; + +CCU_MUX_GATE_DEFINE(CLK_TWSI2, twsi2_clk, twsi2_clk, twsi_parents, + ARRAY_SIZE(twsi_parents), APBC_TWSI2_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +/* + * APBC_TWSI8_CLK_RST has a quirk that reading always results in zero. + * Combine functional and bus bits together as a gate to avoid sharing the + * write-only register between different clock hardwares. + */ +CCU_GATE_DEFINE(CLK_TWSI8, twsi8_clk, twsi8_clk, "pll1_d78_31p5", + APBC_TWSI8_CLK_RST, BIT(1) | BIT(0), 0); + +CCU_MUX_GATE_DEFINE(CLK_UART2, uart2_clk, uart2_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART2_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_UART3, uart3_clk, uart3_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART3_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_UART4, uart4_clk, uart4_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART4_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_UART5, uart5_clk, uart5_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART5_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_UART6, uart6_clk, uart6_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART6_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_UART7, uart7_clk, uart7_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART7_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_UART8, uart8_clk, uart8_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART8_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_UART9, uart9_clk, uart9_clk, uart_clk_parents, + ARRAY_SIZE(uart_clk_parents), APBC_UART9_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); + +CCU_GATE_DEFINE(CLK_GPIO, gpio_clk, gpio_clk, "clock-24m", APBC_GPIO_CLK_RST, + BIT(1) | BIT(0), 0); + +static const char * const pwm_parents[] = { + "pll1_d192_12p8", + "clock-32k", +}; + +CCU_MUX_GATE_DEFINE(CLK_PWM0, pwm0_clk, pwm0_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM0_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM1, pwm1_clk, pwm1_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM1_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM2, pwm2_clk, pwm2_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM2_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM3, pwm3_clk, pwm3_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM3_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM4, pwm4_clk, pwm4_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM4_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM5, pwm5_clk, pwm5_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM5_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM6, pwm6_clk, pwm6_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM6_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM7, pwm7_clk, pwm7_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM7_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM8, pwm8_clk, pwm8_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM8_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM9, pwm9_clk, pwm9_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM9_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM10, pwm10_clk, pwm10_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM10_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM11, pwm11_clk, pwm11_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM11_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM12, pwm12_clk, pwm12_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM12_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM13, pwm13_clk, pwm13_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM13_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM14, pwm14_clk, pwm14_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM14_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM15, pwm15_clk, pwm15_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM15_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM16, pwm16_clk, pwm16_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM16_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM17, pwm17_clk, pwm17_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM17_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM18, pwm18_clk, pwm18_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM18_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_PWM19, pwm19_clk, pwm19_clk, pwm_parents, + ARRAY_SIZE(pwm_parents), APBC_PWM19_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); + +static const char * const ssp_parents[] = { + "pll1_d384_6p4", + "pll1_d192_12p8", + "pll1_d96_25p6", + "pll1_d48_51p2", + "pll1_d768_3p2", + "pll1_d1536_1p6", + "pll1_d3072_0p8", +}; + +CCU_MUX_GATE_DEFINE(CLK_SSP3, ssp3_clk, ssp3_clk, ssp_parents, + ARRAY_SIZE(ssp_parents), APBC_SSP3_CLK_RST, 4, 3, + BIT(1), 0); + +CCU_GATE_DEFINE(CLK_RTC, rtc_clk, rtc_clk, "clock-32k", APBC_RTC_CLK_RST, + BIT(7) | BIT(1) | BIT(0), 0); + +CCU_MUX_GATE_DEFINE(CLK_TWSI0, twsi0_clk, twsi0_clk, twsi_parents, + ARRAY_SIZE(twsi_parents), APBC_TWSI0_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_TWSI1, twsi1_clk, twsi1_clk, twsi_parents, + ARRAY_SIZE(twsi_parents), APBC_TWSI1_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_TWSI4, twsi4_clk, twsi4_clk, twsi_parents, + ARRAY_SIZE(twsi_parents), APBC_TWSI4_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_TWSI5, twsi5_clk, twsi5_clk, twsi_parents, + ARRAY_SIZE(twsi_parents), APBC_TWSI5_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_TWSI6, twsi6_clk, twsi6_clk, twsi_parents, + ARRAY_SIZE(twsi_parents), APBC_TWSI6_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_TWSI7, twsi7_clk, twsi7_clk, twsi_parents, + ARRAY_SIZE(twsi_parents), APBC_TWSI7_CLK_RST, + 4, 3, BIT(1) | BIT(0), 0); + +static const char * const timer_parents[] = { + "pll1_d192_12p8", + "clock-32k", + "pll1_d384_6p4", + "clock-3m", + "clock-1m", +}; + +CCU_MUX_GATE_DEFINE(CLK_TIMERS1, timers1_clk, timers1_clk, timer_parents, + ARRAY_SIZE(timer_parents), APBC_TIMERS1_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); +CCU_MUX_GATE_DEFINE(CLK_TIMERS2, timers2_clk, timers2_clk, timer_parents, + ARRAY_SIZE(timer_parents), APBC_TIMERS2_CLK_RST, 4, 3, + BIT(1) | BIT(0), 0); + +CCU_GATE_DEFINE(CLK_AIB, aib_clk, aib_clk, "clock-24m", APBC_AIB_CLK_RST, + BIT(1) | BIT(0), 0); + +CCU_GATE_DEFINE(CLK_ONEWIRE, onewire_clk, onewire_clk, "clock-24m", + APBC_ONEWIRE_CLK_RST, BIT(1) | BIT(0), 0); + +/* + * When i2s_bclk is selected as the parent clock of sspa, + * the hardware requires bit3 to be set + */ +CCU_GATE_DEFINE(CLK_SSPA0_I2S_BCLK, sspa0_i2s_bclk, sspa0_i2s_bclk, "i2s_bclk", + APBC_SSPA0_CLK_RST, BIT(3), 0); +CCU_GATE_DEFINE(CLK_SSPA1_I2S_BCLK, sspa1_i2s_bclk, sspa1_i2s_bclk, "i2s_bclk", + APBC_SSPA1_CLK_RST, BIT(3), 0); + +static const char * const sspa0_parents[] = { + "pll1_d384_6p4", + "pll1_d192_12p8", + "pll1_d96_25p6", + "pll1_d48_51p2", + "pll1_d768_3p2", + "pll1_d1536_1p6", + "pll1_d3072_0p8", + "sspa0_i2s_bclk", +}; + +CCU_MUX_GATE_DEFINE(CLK_SSPA0, sspa0_clk, sspa0_clk, sspa0_parents, + ARRAY_SIZE(sspa0_parents), APBC_SSPA0_CLK_RST, 4, 3, + BIT(1), 0); + +static const char * const sspa1_parents[] = { + "pll1_d384_6p4", + "pll1_d192_12p8", + "pll1_d96_25p6", + "pll1_d48_51p2", + "pll1_d768_3p2", + "pll1_d1536_1p6", + "pll1_d3072_0p8", + "sspa1_i2s_bclk", +}; + +CCU_MUX_GATE_DEFINE(CLK_SSPA1, sspa1_clk, sspa1_clk, sspa1_parents, + ARRAY_SIZE(sspa1_parents), APBC_SSPA1_CLK_RST, 4, 3, + BIT(1), 0); + +CCU_GATE_DEFINE(CLK_DRO, dro_clk, dro_clk, "apb_clk", APBC_DRO_CLK_RST, + BIT(1) | BIT(0), 0); +CCU_GATE_DEFINE(CLK_IR, ir_clk, ir_clk, "apb_clk", APBC_IR_CLK_RST, + BIT(1) | BIT(0), 0); +CCU_GATE_DEFINE(CLK_TSEN, tsen_clk, tsen_clk, "apb_clk", APBC_TSEN_CLK_RST, + BIT(1) | BIT(0), 0); +CCU_GATE_DEFINE(CLK_IPC_AP2AUD, ipc_ap2aud_clk, ipc_ap2aud_clk, "apb_clk", + APBC_IPC_AP2AUD_CLK_RST, BIT(1) | BIT(0), 0); + +static const char * const can_parents[] = { + "pll3_20", + "pll3_40", + "pll3_80", +}; + +CCU_MUX_GATE_DEFINE(CLK_CAN0, can0_clk, can0_clk, can_parents, + ARRAY_SIZE(can_parents), APBC_CAN0_CLK_RST, 4, 3, + BIT(1), 0); +CCU_GATE_DEFINE(CLK_CAN0_BUS, can0_bus_clk, can0_bus_clk, "clock-24m", + APBC_CAN0_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_UART0_BUS, uart0_bus_clk, uart0_bus_clk, "apb_clk", + APBC_UART1_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_UART2_BUS, uart2_bus_clk, uart2_bus_clk, "apb_clk", + APBC_UART2_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_UART3_BUS, uart3_bus_clk, uart3_bus_clk, "apb_clk", + APBC_UART3_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_UART4_BUS, uart4_bus_clk, uart4_bus_clk, "apb_clk", + APBC_UART4_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_UART5_BUS, uart5_bus_clk, uart5_bus_clk, "apb_clk", + APBC_UART5_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_UART6_BUS, uart6_bus_clk, uart6_bus_clk, "apb_clk", + APBC_UART6_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_UART7_BUS, uart7_bus_clk, uart7_bus_clk, "apb_clk", + APBC_UART7_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_UART8_BUS, uart8_bus_clk, uart8_bus_clk, "apb_clk", + APBC_UART8_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_UART9_BUS, uart9_bus_clk, uart9_bus_clk, "apb_clk", + APBC_UART9_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_GPIO_BUS, gpio_bus_clk, gpio_bus_clk, "apb_clk", + APBC_GPIO_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_PWM0_BUS, pwm0_bus_clk, pwm0_bus_clk, "apb_clk", + APBC_PWM0_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM1_BUS, pwm1_bus_clk, pwm1_bus_clk, "apb_clk", + APBC_PWM1_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM2_BUS, pwm2_bus_clk, pwm2_bus_clk, "apb_clk", + APBC_PWM2_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM3_BUS, pwm3_bus_clk, pwm3_bus_clk, "apb_clk", + APBC_PWM3_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM4_BUS, pwm4_bus_clk, pwm4_bus_clk, "apb_clk", + APBC_PWM4_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM5_BUS, pwm5_bus_clk, pwm5_bus_clk, "apb_clk", + APBC_PWM5_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM6_BUS, pwm6_bus_clk, pwm6_bus_clk, "apb_clk", + APBC_PWM6_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM7_BUS, pwm7_bus_clk, pwm7_bus_clk, "apb_clk", + APBC_PWM7_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM8_BUS, pwm8_bus_clk, pwm8_bus_clk, "apb_clk", + APBC_PWM8_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM9_BUS, pwm9_bus_clk, pwm9_bus_clk, "apb_clk", + APBC_PWM9_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM10_BUS, pwm10_bus_clk, pwm10_bus_clk, "apb_clk", + APBC_PWM10_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM11_BUS, pwm11_bus_clk, pwm11_bus_clk, "apb_clk", + APBC_PWM11_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM12_BUS, pwm12_bus_clk, pwm12_bus_clk, "apb_clk", + APBC_PWM12_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM13_BUS, pwm13_bus_clk, pwm13_bus_clk, "apb_clk", + APBC_PWM13_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM14_BUS, pwm14_bus_clk, pwm14_bus_clk, "apb_clk", + APBC_PWM14_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM15_BUS, pwm15_bus_clk, pwm15_bus_clk, "apb_clk", + APBC_PWM15_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM16_BUS, pwm16_bus_clk, pwm16_bus_clk, "apb_clk", + APBC_PWM16_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM17_BUS, pwm17_bus_clk, pwm17_bus_clk, "apb_clk", + APBC_PWM17_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM18_BUS, pwm18_bus_clk, pwm18_bus_clk, "apb_clk", + APBC_PWM18_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_PWM19_BUS, pwm19_bus_clk, pwm19_bus_clk, "apb_clk", + APBC_PWM19_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_SSP3_BUS, ssp3_bus_clk, ssp3_bus_clk, "apb_clk", + APBC_SSP3_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_RTC_BUS, rtc_bus_clk, rtc_bus_clk, "apb_clk", + APBC_RTC_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_TWSI0_BUS, twsi0_bus_clk, twsi0_bus_clk, "apb_clk", + APBC_TWSI0_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_TWSI1_BUS, twsi1_bus_clk, twsi1_bus_clk, "apb_clk", + APBC_TWSI1_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_TWSI2_BUS, twsi2_bus_clk, twsi2_bus_clk, "apb_clk", + APBC_TWSI2_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_TWSI4_BUS, twsi4_bus_clk, twsi4_bus_clk, "apb_clk", + APBC_TWSI4_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_TWSI5_BUS, twsi5_bus_clk, twsi5_bus_clk, "apb_clk", + APBC_TWSI5_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_TWSI6_BUS, twsi6_bus_clk, twsi6_bus_clk, "apb_clk", + APBC_TWSI6_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_TWSI7_BUS, twsi7_bus_clk, twsi7_bus_clk, "apb_clk", + APBC_TWSI7_CLK_RST, BIT(0), 0); +CCU_FACTOR_DEFINE(CLK_TWSI8_BUS, twsi8_bus_clk, twsi8_bus_clk, "apb_clk", 1, 1); + +CCU_GATE_DEFINE(CLK_TIMERS1_BUS, timers1_bus_clk, timers1_bus_clk, "apb_clk", + APBC_TIMERS1_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_TIMERS2_BUS, timers2_bus_clk, timers2_bus_clk, "apb_clk", + APBC_TIMERS2_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_AIB_BUS, aib_bus_clk, aib_bus_clk, "apb_clk", + APBC_AIB_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_ONEWIRE_BUS, onewire_bus_clk, onewire_bus_clk, "apb_clk", + APBC_ONEWIRE_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_SSPA0_BUS, sspa0_bus_clk, sspa0_bus_clk, "apb_clk", + APBC_SSPA0_CLK_RST, BIT(0), 0); +CCU_GATE_DEFINE(CLK_SSPA1_BUS, sspa1_bus_clk, sspa1_bus_clk, "apb_clk", + APBC_SSPA1_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_TSEN_BUS, tsen_bus_clk, tsen_bus_clk, "apb_clk", + APBC_TSEN_CLK_RST, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_IPC_AP2AUD_BUS, ipc_ap2aud_bus_clk, ipc_ap2aud_bus_clk, + "apb_clk", APBC_IPC_AP2AUD_CLK_RST, BIT(0), 0); +#endif +/* APBC clocks end */ + +/* APMU clocks start */ +#if IS_ENABLED(CONFIG_SPL_BUILD) +static const char * const pmua_aclk_parents[] = { + "clock-dummy", + "pll1_d8_307p2", +}; + +CCU_MUX_DIV_FC_DEFINE(CLK_PMUA_ACLK, pmua_aclk, pmua_aclk, pmua_aclk_parents, + ARRAY_SIZE(pmua_aclk_parents), + APMU_ACLK_CLK_CTRL, APMU_ACLK_CLK_CTRL, 1, 2, BIT(4), + 0, 1, 0); + +CCU_GATE_DEFINE(CLK_SDH_AXI, sdh_axi_aclk, sdh_axi_aclk, "pmua_aclk", + APMU_SDH0_CLK_RES_CTRL, BIT(3), 0); + +static const char * const sdh01_parents[] = { + "pll1_d6_409p6", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH0, sdh0_clk, sdh0_clk, sdh01_parents, + ARRAY_SIZE(sdh01_parents), + APMU_SDH0_CLK_RES_CTRL, + APMU_SDH0_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3, + BIT(4), 0); + +static const char * const sdh2_parents[] = { + "pll1_d6_409p6", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH2, sdh2_clk, sdh2_clk, sdh2_parents, + ARRAY_SIZE(sdh2_parents), + APMU_SDH2_CLK_RES_CTRL, + APMU_SDH2_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3, + BIT(4), 0); +#else +static const char * const pmua_aclk_parents[] = { + "pll1_d10_245p76", + "pll1_d8_307p2", +}; + +CCU_MUX_DIV_FC_DEFINE(CLK_PMUA_ACLK, pmua_aclk, pmua_aclk, pmua_aclk_parents, + ARRAY_SIZE(pmua_aclk_parents), + APMU_ACLK_CLK_CTRL, APMU_ACLK_CLK_CTRL, 1, 2, BIT(4), + 0, 1, 0); + +static const char * const emmc_parents[] = { + "pll1_d6_409p6", + "pll1_d4_614p4", + "pll1_d52_47p26", + "pll1_d3_819p2", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_EMMC, emmc_clk, emmc_clk, emmc_parents, + ARRAY_SIZE(emmc_parents), + APMU_PMUA_EM_CLK_RES_CTRL, + APMU_PMUA_EM_CLK_RES_CTRL, 8, 3, BIT(11), + 6, 2, BIT(4), 0); +CCU_DIV_GATE_DEFINE(CLK_EMMC_X, emmc_x_clk, emmc_x_clk, "pll1_d2_1228p8", + APMU_PMUA_EM_CLK_RES_CTRL, 12, + 3, BIT(15), 0); + +CCU_GATE_DEFINE(CLK_EMMC_BUS, emmc_bus_clk, emmc_bus_clk, "pmua_aclk", + APMU_PMUA_EM_CLK_RES_CTRL, BIT(3), 0); + +static const char * const cci550_clk_parents[] = { + "pll1_d5_491p52", + "pll1_d4_614p4", + "pll1_d3_819p2", + "pll2_d3", +}; + +CCU_MUX_DIV_FC_DEFINE(CLK_CCI550, cci550_clk, cci550_clk, cci550_clk_parents, + ARRAY_SIZE(cci550_clk_parents), + APMU_CCI550_CLK_CTRL, APMU_CCI550_CLK_CTRL, 8, 3, + BIT(12), 0, 2, CLK_IS_CRITICAL); + +static const char * const cpu_c0_hi_clk_parents[] = { + "pll3_d2", + "pll3_d1", +}; + +CCU_MUX_DEFINE(CLK_CPU_C0_HI, cpu_c0_hi_clk, cpu_c0_hi_clk, + cpu_c0_hi_clk_parents, ARRAY_SIZE(cpu_c0_hi_clk_parents), + APMU_CPU_C0_CLK_CTRL, 13, 1, 0); +static const char * const cpu_c0_clk_parents[] = { + "pll1_d4_614p4", + "pll1_d3_819p2", + "pll1_d6_409p6", + "pll1_d5_491p52", + "pll1_d2_1228p8", + "pll3_d3", + "pll2_d3", + "cpu_c0_hi_clk", +}; + +CCU_MUX_FC_DEFINE(CLK_CPU_C0_CORE, cpu_c0_core_clk, cpu_c0_core_clk, + cpu_c0_clk_parents, ARRAY_SIZE(cpu_c0_clk_parents), + APMU_CPU_C0_CLK_CTRL, APMU_CPU_C0_CLK_CTRL, + BIT(12), 0, 3, CLK_IS_CRITICAL); +CCU_DIV_DEFINE(CLK_CPU_C0_ACE, cpu_c0_ace_clk, cpu_c0_ace_clk, + "cpu_c0_core_clk", APMU_CPU_C0_CLK_CTRL, 6, 3, CLK_IS_CRITICAL); +CCU_DIV_DEFINE(CLK_CPU_C0_TCM, cpu_c0_tcm_clk, cpu_c0_tcm_clk, + "cpu_c0_core_clk", APMU_CPU_C0_CLK_CTRL, 9, 3, CLK_IS_CRITICAL); + +static const char * const cpu_c1_hi_clk_parents[] = { + "pll3_d2", + "pll3_d1", +}; + +CCU_MUX_DEFINE(CLK_CPU_C1_HI, cpu_c1_hi_clk, cpu_c1_hi_clk, + cpu_c1_hi_clk_parents, ARRAY_SIZE(cpu_c1_hi_clk_parents), + APMU_CPU_C1_CLK_CTRL, 13, 1, 0); +static const char * const cpu_c1_clk_parents[] = { + "pll1_d4_614p4", + "pll1_d3_819p2", + "pll1_d6_409p6", + "pll1_d5_491p52", + "pll1_d2_1228p8", + "pll3_d3", + "pll2_d3", + "cpu_c1_hi_clk", +}; + +CCU_MUX_FC_DEFINE(CLK_CPU_C1_CORE, cpu_c1_core_clk, cpu_c1_core_clk, + cpu_c1_clk_parents, ARRAY_SIZE(cpu_c1_clk_parents), + APMU_CPU_C1_CLK_CTRL, APMU_CPU_C1_CLK_CTRL, + BIT(12), 0, 3, CLK_IS_CRITICAL); +CCU_DIV_DEFINE(CLK_CPU_C1_ACE, cpu_c1_ace_clk, cpu_c1_ace_clk, + "cpu_c1_core_clk", APMU_CPU_C1_CLK_CTRL, 6, 3, CLK_IS_CRITICAL); + +static const char * const jpg_parents[] = { + "pll1_d4_614p4", + "pll1_d6_409p6", + "pll1_d5_491p52", + "pll1_d3_819p2", + "pll1_d2_1228p8", + "pll2_d4", + "pll2_d3", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_JPG, jpg_clk, jpg_clk, jpg_parents, + ARRAY_SIZE(jpg_parents), + APMU_JPG_CLK_RES_CTRL, + APMU_JPG_CLK_RES_CTRL, + 5, 3, BIT(15), 2, 3, BIT(1), 0); + +static const char * const ccic2phy_parents[] = { + "pll1_d24_102p4", + "pll1_d48_51p2_ap", +}; + +CCU_MUX_GATE_DEFINE(CLK_CCIC2PHY, ccic2phy_clk, ccic2phy_clk, ccic2phy_parents, + ARRAY_SIZE(ccic2phy_parents), APMU_CSI_CCIC2_CLK_RES_CTRL, + 7, 1, BIT(5), 0); + +static const char * const ccic3phy_parents[] = { + "pll1_d24_102p4", + "pll1_d48_51p2_ap", +}; + +CCU_MUX_GATE_DEFINE(CLK_CCIC3PHY, ccic3phy_clk, ccic3phy_clk, ccic3phy_parents, + ARRAY_SIZE(ccic3phy_parents), APMU_CSI_CCIC2_CLK_RES_CTRL, + 31, 1, BIT(30), 0); + +static const char * const csi_parents[] = { + "pll1_d5_491p52", + "pll1_d6_409p6", + "pll1_d4_614p4", + "pll1_d3_819p2", + "pll2_d2", + "pll2_d3", + "pll2_d4", + "pll1_d2_1228p8", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_CSI, csi_clk, csi_clk, csi_parents, + ARRAY_SIZE(csi_parents), + APMU_CSI_CCIC2_CLK_RES_CTRL, + APMU_CSI_CCIC2_CLK_RES_CTRL, 20, 3, BIT(15), + 16, 3, BIT(4), 0); + +static const char * const camm_parents[] = { + "pll1_d8_307p2", + "pll2_d5", + "pll1_d6_409p6", + "clock-24m", +}; + +CCU_MUX_DIV_GATE_DEFINE(CLK_CAMM0, camm0_clk, camm0_clk, camm_parents, + ARRAY_SIZE(camm_parents), + APMU_CSI_CCIC2_CLK_RES_CTRL, 23, 4, 8, 2, + BIT(28), 0); +CCU_MUX_DIV_GATE_DEFINE(CLK_CAMM1, camm1_clk, camm1_clk, camm_parents, + ARRAY_SIZE(camm_parents), + APMU_CSI_CCIC2_CLK_RES_CTRL, 23, 4, 8, 2, + BIT(6), 0); +CCU_MUX_DIV_GATE_DEFINE(CLK_CAMM2, camm2_clk, camm2_clk, camm_parents, + ARRAY_SIZE(camm_parents), + APMU_CSI_CCIC2_CLK_RES_CTRL, 23, 4, 8, 2, + BIT(3), 0); + +static const char * const isp_cpp_parents[] = { + "pll1_d8_307p2", + "pll1_d6_409p6", +}; + +CCU_MUX_DIV_GATE_DEFINE(CLK_ISP_CPP, isp_cpp_clk, isp_cpp_clk, isp_cpp_parents, + ARRAY_SIZE(isp_cpp_parents), + APMU_ISP_CLK_RES_CTRL, 24, 2, 26, 1, + BIT(28), 0); +static const char * const isp_bus_parents[] = { + "pll1_d6_409p6", + "pll1_d5_491p52", + "pll1_d8_307p2", + "pll1_d10_245p76", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_ISP_BUS, isp_bus_clk, isp_bus_clk, + isp_bus_parents, ARRAY_SIZE(isp_cpp_parents), + APMU_ISP_CLK_RES_CTRL, + APMU_ISP_CLK_RES_CTRL, 18, 3, BIT(23), + 21, 2, BIT(17), 0); +static const char * const isp_parents[] = { + "pll1_d6_409p6", + "pll1_d5_491p52", + "pll1_d4_614p4", + "pll1_d8_307p2", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_ISP, isp_clk, isp_clk, isp_parents, + ARRAY_SIZE(isp_parents), + APMU_ISP_CLK_RES_CTRL, + APMU_ISP_CLK_RES_CTRL, + 4, 3, BIT(7), 8, 2, BIT(1), 0); + +static const char * const dpumclk_parents[] = { + "pll1_d6_409p6", + "pll1_d5_491p52", + "pll1_d4_614p4", + "pll1_d8_307p2", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_DPU_MCLK, dpu_mclk, dpu_mclk, + dpumclk_parents, ARRAY_SIZE(dpumclk_parents), + APMU_LCD_CLK_RES_CTRL2, APMU_LCD_CLK_RES_CTRL1, + 1, 4, BIT(29), 5, 3, BIT(0), 0); + +static const char * const dpuesc_parents[] = { + "pll1_d48_51p2_ap", + "pll1_d52_47p26", + "pll1_d96_25p6", + "pll1_d32_76p8", +}; + +CCU_MUX_GATE_DEFINE(CLK_DPU_ESC, dpu_esc_clk, dpu_esc_clk, dpuesc_parents, + ARRAY_SIZE(dpuesc_parents), APMU_LCD_CLK_RES_CTRL1, 0, 2, + BIT(2), 0); + +static const char * const dpubit_parents[] = { + "pll1_d3_819p2", + "pll2_d2", + "pll2_d3", + "pll1_d2_1228p8", + "pll2_d4", + "pll2_d5", + "pll2_d7", + "pll2_d8", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_DPU_BIT, dpu_bit_clk, dpu_bit_clk, + dpubit_parents, ARRAY_SIZE(dpubit_parents), + APMU_LCD_CLK_RES_CTRL1, + APMU_LCD_CLK_RES_CTRL1, 17, 3, BIT(31), + 20, 3, BIT(16), 0); + +static const char * const dpupx_parents[] = { + "pll1_d6_409p6", + "pll1_d5_491p52", + "pll1_d4_614p4", + "pll1_d8_307p2", + "pll2_d7", + "pll2_d8", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_DPU_PXCLK, dpu_pxclk, dpu_pxclk, + dpupx_parents, ARRAY_SIZE(dpupx_parents), + APMU_LCD_CLK_RES_CTRL2, APMU_LCD_CLK_RES_CTRL1, + 17, 4, BIT(30), 21, 3, BIT(16), 0); + +CCU_GATE_DEFINE(CLK_DPU_HCLK, dpu_hclk, dpu_hclk, "pmua_aclk", + APMU_LCD_CLK_RES_CTRL1, BIT(5), 0); + +static const char * const dpu_spi_parents[] = { + "pll1_d8_307p2", + "pll1_d6_409p6", + "pll1_d10_245p76", + "pll1_d11_223p4", + "pll1_d13_189", + "pll1_d23_106p8", + "pll2_d3", + "pll2_d5", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_DPU_SPI, dpu_spi_clk, dpu_spi_clk, + dpu_spi_parents, ARRAY_SIZE(dpu_spi_parents), + APMU_LCD_SPI_CLK_RES_CTRL, + APMU_LCD_SPI_CLK_RES_CTRL, 8, 3, + BIT(7), 12, 3, BIT(1), 0); +CCU_GATE_DEFINE(CLK_DPU_SPI_HBUS, dpu_spi_hbus_clk, dpu_spi_hbus_clk, + "pmua_aclk", APMU_LCD_SPI_CLK_RES_CTRL, BIT(3), 0); +CCU_GATE_DEFINE(CLK_DPU_SPIBUS, dpu_spi_bus_clk, dpu_spi_bus_clk, + "pmua_aclk", APMU_LCD_SPI_CLK_RES_CTRL, BIT(5), 0); +CCU_GATE_DEFINE(CLK_DPU_SPI_ACLK, dpu_spi_aclk, dpu_spi_aclk, + "pmua_aclk", APMU_LCD_SPI_CLK_RES_CTRL, BIT(6), 0); + +static const char * const v2d_parents[] = { + "pll1_d5_491p52", + "pll1_d6_409p6", + "pll1_d8_307p2", + "pll1_d4_614p4", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_V2D, v2d_clk, v2d_clk, v2d_parents, + ARRAY_SIZE(v2d_parents), + APMU_LCD_CLK_RES_CTRL1, + APMU_LCD_CLK_RES_CTRL1, 9, 3, BIT(28), 12, 2, + BIT(8), 0); + +static const char * const ccic_4x_parents[] = { + "pll1_d5_491p52", + "pll1_d6_409p6", + "pll1_d4_614p4", + "pll1_d3_819p2", + "pll2_d2", + "pll2_d3", + "pll2_d4", + "pll1_d2_1228p8", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_CCIC_4X, ccic_4x_clk, ccic_4x_clk, + ccic_4x_parents, ARRAY_SIZE(ccic_4x_parents), + APMU_CCIC_CLK_RES_CTRL, + APMU_CCIC_CLK_RES_CTRL, 18, 3, + BIT(15), 23, 2, BIT(4), 0); + +static const char * const ccic1phy_parents[] = { + "pll1_d24_102p4", + "pll1_d48_51p2_ap", +}; + +CCU_MUX_GATE_DEFINE(CLK_CCIC1PHY, ccic1phy_clk, ccic1phy_clk, ccic1phy_parents, + ARRAY_SIZE(ccic1phy_parents), APMU_CCIC_CLK_RES_CTRL, 7, 1, + BIT(5), 0); + +CCU_GATE_DEFINE(CLK_SDH_AXI, sdh_axi_aclk, sdh_axi_aclk, "pmua_aclk", + APMU_SDH0_CLK_RES_CTRL, BIT(3), 0); +static const char * const sdh01_parents[] = { + "pll1_d6_409p6", + "pll1_d4_614p4", + "pll2_d8", + "pll2_d5", + "pll1_d11_223p4", + "pll1_d13_189", + "pll1_d23_106p8", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH0, sdh0_clk, sdh0_clk, sdh01_parents, + ARRAY_SIZE(sdh01_parents), + APMU_SDH0_CLK_RES_CTRL, + APMU_SDH0_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3, + BIT(4), 0); +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH1, sdh1_clk, sdh1_clk, sdh01_parents, + ARRAY_SIZE(sdh01_parents), + APMU_SDH1_CLK_RES_CTRL, + APMU_SDH1_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3, + BIT(4), 0); +static const char * const sdh2_parents[] = { + "pll1_d6_409p6", + "pll1_d4_614p4", + "pll2_d8", + "pll1_d3_819p2", + "pll1_d11_223p4", + "pll1_d13_189", + "pll1_d23_106p8", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH2, sdh2_clk, sdh2_clk, sdh2_parents, + ARRAY_SIZE(sdh2_parents), + APMU_SDH2_CLK_RES_CTRL, + APMU_SDH2_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3, + BIT(4), 0); + +CCU_GATE_DEFINE(CLK_USB_AXI, usb_axi_clk, usb_axi_clk, "pmua_aclk", + APMU_USB_CLK_RES_CTRL, BIT(1), 0); +CCU_GATE_DEFINE(CLK_USB_P1, usb_p1_aclk, usb_p1_aclk, "pmua_aclk", + APMU_USB_CLK_RES_CTRL, BIT(5), 0); +CCU_GATE_DEFINE(CLK_USB30, usb30_clk, usb30_clk, "pmua_aclk", + APMU_USB_CLK_RES_CTRL, BIT(8), 0); + +static const char * const qspi_parents[] = { + "pll1_d6_409p6", + "pll2_d8", + "pll1_d8_307p2", + "pll1_d10_245p76", + "pll1_d11_223p4", + "pll1_d23_106p8", + "pll1_d5_491p52", + "pll1_d13_189", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_QSPI, qspi_clk, qspi_clk, qspi_parents, + ARRAY_SIZE(qspi_parents), + APMU_QSPI_CLK_RES_CTRL, + APMU_QSPI_CLK_RES_CTRL, 9, 3, BIT(12), 6, 3, + BIT(4), 0); +CCU_GATE_DEFINE(CLK_QSPI_BUS, qspi_bus_clk, qspi_bus_clk, "pmua_aclk", + APMU_QSPI_CLK_RES_CTRL, BIT(3), 0); +CCU_GATE_DEFINE(CLK_DMA, dma_clk, dma_clk, "pmua_aclk", APMU_DMA_CLK_RES_CTRL, + BIT(3), 0); + +static const char * const aes_parents[] = { + "pll1_d12_204p8", + "pll1_d24_102p4", +}; + +CCU_MUX_GATE_DEFINE(CLK_AES, aes_clk, aes_clk, aes_parents, + ARRAY_SIZE(aes_parents), APMU_AES_CLK_RES_CTRL, 6, 1, + BIT(5), 0); + +static const char * const vpu_parents[] = { + "pll1_d4_614p4", + "pll1_d5_491p52", + "pll1_d3_819p2", + "pll1_d6_409p6", + "pll3_d6", + "pll2_d3", + "pll2_d4", + "pll2_d5", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_VPU, vpu_clk, vpu_clk, vpu_parents, + ARRAY_SIZE(vpu_parents), APMU_VPU_CLK_RES_CTRL, + APMU_VPU_CLK_RES_CTRL, 13, 3, BIT(21), 10, 3, + BIT(3), 0); + +static const char * const gpu_parents[] = { + "pll1_d4_614p4", + "pll1_d5_491p52", + "pll1_d3_819p2", + "pll1_d6_409p6", + "pll3_d6", + "pll2_d3", + "pll2_d4", + "pll2_d5", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_GPU, gpu_clk, gpu_clk, gpu_parents, + ARRAY_SIZE(gpu_parents), APMU_GPU_CLK_RES_CTRL, + APMU_GPU_CLK_RES_CTRL, 12, 3, BIT(15), 18, 3, + BIT(4), 0); + +static const char * const audio_parents[] = { + "pll1_aud_245p7", + "pll1_d8_307p2", + "pll1_d6_409p6", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_AUDIO, audio_clk, audio_clk, audio_parents, + ARRAY_SIZE(audio_parents), + APMU_AUDIO_CLK_RES_CTRL, + APMU_AUDIO_CLK_RES_CTRL, 4, 3, BIT(15), + 7, 3, BIT(12), 0); + +static const char * const hdmi_parents[] = { + "pll1_d6_409p6", + "pll1_d5_491p52", + "pll1_d4_614p4", + "pll1_d8_307p2", +}; + +CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_HDMI, hdmi_mclk, hdmi_mclk, hdmi_parents, + ARRAY_SIZE(hdmi_parents), + APMU_HDMI_CLK_RES_CTRL, + APMU_HDMI_CLK_RES_CTRL, 1, 4, BIT(29), 5, + 3, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_PCIE0_MASTER, pcie0_master_clk, pcie0_master_clk, + "pmua_aclk", APMU_PCIE_CLK_RES_CTRL_0, BIT(2), 0); +CCU_GATE_DEFINE(CLK_PCIE0_SLAVE, pcie0_slave_clk, pcie0_slave_clk, "pmua_aclk", + APMU_PCIE_CLK_RES_CTRL_0, BIT(1), 0); +CCU_GATE_DEFINE(CLK_PCIE0_DBI, pcie0_dbi_clk, pcie0_dbi_clk, "pmua_aclk", + APMU_PCIE_CLK_RES_CTRL_0, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_PCIE1_MASTER, pcie1_master_clk, pcie1_master_clk, + "pmua_aclk", APMU_PCIE_CLK_RES_CTRL_1, BIT(2), 0); +CCU_GATE_DEFINE(CLK_PCIE1_SLAVE, pcie1_slave_clk, pcie1_slave_clk, "pmua_aclk", + APMU_PCIE_CLK_RES_CTRL_1, BIT(1), 0); +CCU_GATE_DEFINE(CLK_PCIE1_DBI, pcie1_dbi_clk, pcie1_dbi_clk, "pmua_aclk", + APMU_PCIE_CLK_RES_CTRL_1, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_PCIE2_MASTER, pcie2_master_clk, pcie2_master_clk, + "pmua_aclk", APMU_PCIE_CLK_RES_CTRL_2, BIT(2), 0); +CCU_GATE_DEFINE(CLK_PCIE2_SLAVE, pcie2_slave_clk, pcie2_slave_clk, "pmua_aclk", + APMU_PCIE_CLK_RES_CTRL_2, BIT(1), 0); +CCU_GATE_DEFINE(CLK_PCIE2_DBI, pcie2_dbi_clk, pcie2_dbi_clk, "pmua_aclk", + APMU_PCIE_CLK_RES_CTRL_2, BIT(0), 0); + +CCU_GATE_DEFINE(CLK_EMAC0_BUS, emac0_bus_clk, emac0_bus_clk, "pmua_aclk", + APMU_EMAC0_CLK_RES_CTRL, BIT(0), 0); +CCU_GATE_DEFINE(CLK_EMAC0_PTP, emac0_ptp_clk, emac0_ptp_clk, "pll2_d6", + APMU_EMAC0_CLK_RES_CTRL, BIT(15), 0); +CCU_GATE_DEFINE(CLK_EMAC1_BUS, emac1_bus_clk, emac1_bus_clk, "pmua_aclk", + APMU_EMAC1_CLK_RES_CTRL, BIT(0), 0); +CCU_GATE_DEFINE(CLK_EMAC1_PTP, emac1_ptp_clk, emac1_ptp_clk, "pll2_d6", + APMU_EMAC1_CLK_RES_CTRL, BIT(15), 0); + +#endif +/* APMU clocks end */ + +#if IS_ENABLED(CONFIG_SPL_BUILD) +static struct clk *k1_ccu_pll_clks[] = { + &pll1.common.clk, + &pll1_d4.common.clk, + &pll1_d6.common.clk, + &pll1_d8.common.clk, +}; +#else +static struct clk *k1_ccu_pll_clks[] = { + &pll1.common.clk, + &pll2.common.clk, + &pll3.common.clk, + &pll1_d2.common.clk, + &pll1_d3.common.clk, + &pll1_d4.common.clk, + &pll1_d5.common.clk, + &pll1_d6.common.clk, + &pll1_d7.common.clk, + &pll1_d8.common.clk, + &pll1_d11_223p4.common.clk, + &pll1_d13_189.common.clk, + &pll1_d23_106p8.common.clk, + &pll1_d64_38p4.common.clk, + &pll1_aud_245p7.common.clk, + &pll1_aud_24p5.common.clk, + &pll2_d1.common.clk, + &pll2_d2.common.clk, + &pll2_d3.common.clk, + &pll2_d4.common.clk, + &pll2_d5.common.clk, + &pll2_d6.common.clk, + &pll2_d7.common.clk, + &pll2_d8.common.clk, + &pll3_d1.common.clk, + &pll3_d2.common.clk, + &pll3_d3.common.clk, + &pll3_d4.common.clk, + &pll3_d5.common.clk, + &pll3_d6.common.clk, + &pll3_d7.common.clk, + &pll3_d8.common.clk, + &pll3_80.common.clk, + &pll3_40.common.clk, + &pll3_20.common.clk, +}; +#endif + +static const struct spacemit_ccu_data k1_ccu_pll_data = { + .clks = k1_ccu_pll_clks, + .num = ARRAY_SIZE(k1_ccu_pll_clks), + .offset = K1_PLL_ID, +}; + +#if IS_ENABLED(CONFIG_SPL_BUILD) +static struct clk *k1_ccu_mpmu_clks[] = { + &pll1_d4_614p4.common.clk, + &pll1_d6_409p6.common.clk, + &pll1_d8_307p2.common.clk, + &pll1_d78_31p5.common.clk, + &slow_uart2_48.common.clk, +}; +#else +static struct clk *k1_ccu_mpmu_clks[] = { + &pll1_d8_307p2.common.clk, + &pll1_d32_76p8.common.clk, + &pll1_d40_61p44.common.clk, + &pll1_d16_153p6.common.clk, + &pll1_d24_102p4.common.clk, + &pll1_d48_51p2.common.clk, + &pll1_d48_51p2_ap.common.clk, + &pll1_m3d128_57p6.common.clk, + &pll1_d96_25p6.common.clk, + &pll1_d192_12p8.common.clk, + &pll1_d192_12p8_wdt.common.clk, + &pll1_d384_6p4.common.clk, + &pll1_d768_3p2.common.clk, + &pll1_d1536_1p6.common.clk, + &pll1_d3072_0p8.common.clk, + &pll1_d6_409p6.common.clk, + &pll1_d12_204p8.common.clk, + &pll1_d5_491p52.common.clk, + &pll1_d10_245p76.common.clk, + &pll1_d4_614p4.common.clk, + &pll1_d52_47p26.common.clk, + &pll1_d78_31p5.common.clk, + &pll1_d3_819p2.common.clk, + &pll1_d2_1228p8.common.clk, + &slow_uart.common.clk, + &slow_uart1_14p74.common.clk, + &slow_uart2_48.common.clk, + &wdt_clk.common.clk, + &apb_clk.common.clk, + &ripc_clk.common.clk, + &i2s_153p6.common.clk, + &i2s_153p6_base.common.clk, + &i2s_sysclk_src.common.clk, + &i2s_sysclk.common.clk, + &i2s_bclk_factor.common.clk, + &i2s_bclk.common.clk, + &wdt_bus_clk.common.clk, +}; +#endif + +static const struct spacemit_ccu_data k1_ccu_mpmu_data = { + .clks = k1_ccu_mpmu_clks, + .num = ARRAY_SIZE(k1_ccu_mpmu_clks), + .offset = K1_MPMU_ID, +}; + +#if IS_ENABLED(CONFIG_SPL_BUILD) +static struct clk *k1_ccu_apbc_clks[] = { + &uart0_clk.common.clk, + &twsi2_clk.common.clk, + &twsi8_clk.common.clk, +}; +#else +static struct clk *k1_ccu_apbc_clks[] = { + &uart0_clk.common.clk, + &uart2_clk.common.clk, + &uart3_clk.common.clk, + &uart4_clk.common.clk, + &uart5_clk.common.clk, + &uart6_clk.common.clk, + &uart7_clk.common.clk, + &uart8_clk.common.clk, + &uart9_clk.common.clk, + &gpio_clk.common.clk, + &pwm0_clk.common.clk, + &pwm1_clk.common.clk, + &pwm2_clk.common.clk, + &pwm3_clk.common.clk, + &pwm4_clk.common.clk, + &pwm5_clk.common.clk, + &pwm6_clk.common.clk, + &pwm7_clk.common.clk, + &pwm8_clk.common.clk, + &pwm9_clk.common.clk, + &pwm10_clk.common.clk, + &pwm11_clk.common.clk, + &pwm12_clk.common.clk, + &pwm13_clk.common.clk, + &pwm14_clk.common.clk, + &pwm15_clk.common.clk, + &pwm16_clk.common.clk, + &pwm17_clk.common.clk, + &pwm18_clk.common.clk, + &pwm19_clk.common.clk, + &ssp3_clk.common.clk, + &rtc_clk.common.clk, + &twsi0_clk.common.clk, + &twsi1_clk.common.clk, + &twsi2_clk.common.clk, + &twsi4_clk.common.clk, + &twsi5_clk.common.clk, + &twsi6_clk.common.clk, + &twsi7_clk.common.clk, + &twsi8_clk.common.clk, + &timers1_clk.common.clk, + &timers2_clk.common.clk, + &aib_clk.common.clk, + &onewire_clk.common.clk, + &sspa0_clk.common.clk, + &sspa1_clk.common.clk, + &dro_clk.common.clk, + &ir_clk.common.clk, + &tsen_clk.common.clk, + &ipc_ap2aud_clk.common.clk, + &can0_clk.common.clk, + &can0_bus_clk.common.clk, + &uart0_bus_clk.common.clk, + &uart2_bus_clk.common.clk, + &uart3_bus_clk.common.clk, + &uart4_bus_clk.common.clk, + &uart5_bus_clk.common.clk, + &uart6_bus_clk.common.clk, + &uart7_bus_clk.common.clk, + &uart8_bus_clk.common.clk, + &uart9_bus_clk.common.clk, + &gpio_bus_clk.common.clk, + &pwm0_bus_clk.common.clk, + &pwm1_bus_clk.common.clk, + &pwm2_bus_clk.common.clk, + &pwm3_bus_clk.common.clk, + &pwm4_bus_clk.common.clk, + &pwm5_bus_clk.common.clk, + &pwm6_bus_clk.common.clk, + &pwm7_bus_clk.common.clk, + &pwm8_bus_clk.common.clk, + &pwm9_bus_clk.common.clk, + &pwm10_bus_clk.common.clk, + &pwm11_bus_clk.common.clk, + &pwm12_bus_clk.common.clk, + &pwm13_bus_clk.common.clk, + &pwm14_bus_clk.common.clk, + &pwm15_bus_clk.common.clk, + &pwm16_bus_clk.common.clk, + &pwm17_bus_clk.common.clk, + &pwm18_bus_clk.common.clk, + &pwm19_bus_clk.common.clk, + &ssp3_bus_clk.common.clk, + &rtc_bus_clk.common.clk, + &twsi0_bus_clk.common.clk, + &twsi1_bus_clk.common.clk, + &twsi2_bus_clk.common.clk, + &twsi4_bus_clk.common.clk, + &twsi5_bus_clk.common.clk, + &twsi6_bus_clk.common.clk, + &twsi7_bus_clk.common.clk, + &twsi8_bus_clk.common.clk, + &timers1_bus_clk.common.clk, + &timers2_bus_clk.common.clk, + &aib_bus_clk.common.clk, + &onewire_bus_clk.common.clk, + &sspa0_bus_clk.common.clk, + &sspa1_bus_clk.common.clk, + &tsen_bus_clk.common.clk, + &ipc_ap2aud_bus_clk.common.clk, + &sspa0_i2s_bclk.common.clk, + &sspa1_i2s_bclk.common.clk, +}; +#endif + +static const struct spacemit_ccu_data k1_ccu_apbc_data = { + .clks = k1_ccu_apbc_clks, + .num = ARRAY_SIZE(k1_ccu_apbc_clks), + .offset = K1_APBC_ID, +}; + +#if IS_ENABLED(CONFIG_SPL_BUILD) +static struct clk *k1_ccu_apmu_clks[] = { + &pmua_aclk.common.clk, + &sdh_axi_aclk.common.clk, + &sdh0_clk.common.clk, + &sdh2_clk.common.clk, +}; +#else +static struct clk *k1_ccu_apmu_clks[] = { + &cci550_clk.common.clk, + &cpu_c0_hi_clk.common.clk, + &cpu_c0_core_clk.common.clk, + &cpu_c0_ace_clk.common.clk, + &cpu_c0_tcm_clk.common.clk, + &cpu_c1_hi_clk.common.clk, + &cpu_c1_core_clk.common.clk, + &cpu_c1_ace_clk.common.clk, + &ccic_4x_clk.common.clk, + &ccic1phy_clk.common.clk, + &pmua_aclk.common.clk, + &sdh_axi_aclk.common.clk, + &sdh0_clk.common.clk, + &sdh1_clk.common.clk, + &sdh2_clk.common.clk, + &usb_p1_aclk.common.clk, + &usb_axi_clk.common.clk, + &usb30_clk.common.clk, + &qspi_clk.common.clk, + &qspi_bus_clk.common.clk, + &dma_clk.common.clk, + &aes_clk.common.clk, + &vpu_clk.common.clk, + &gpu_clk.common.clk, + &emmc_clk.common.clk, + &emmc_x_clk.common.clk, + &audio_clk.common.clk, + &hdmi_mclk.common.clk, + &pcie0_master_clk.common.clk, + &pcie0_slave_clk.common.clk, + &pcie0_dbi_clk.common.clk, + &pcie1_master_clk.common.clk, + &pcie1_slave_clk.common.clk, + &pcie1_dbi_clk.common.clk, + &pcie2_master_clk.common.clk, + &pcie2_slave_clk.common.clk, + &pcie2_dbi_clk.common.clk, + &emac0_bus_clk.common.clk, + &emac0_ptp_clk.common.clk, + &emac1_bus_clk.common.clk, + &emac1_ptp_clk.common.clk, + &jpg_clk.common.clk, + &ccic2phy_clk.common.clk, + &ccic3phy_clk.common.clk, + &csi_clk.common.clk, + &camm0_clk.common.clk, + &camm1_clk.common.clk, + &camm2_clk.common.clk, + &isp_cpp_clk.common.clk, + &isp_bus_clk.common.clk, + &isp_clk.common.clk, + &dpu_mclk.common.clk, + &dpu_esc_clk.common.clk, + &dpu_bit_clk.common.clk, + &dpu_pxclk.common.clk, + &dpu_hclk.common.clk, + &dpu_spi_clk.common.clk, + &dpu_spi_hbus_clk.common.clk, + &dpu_spi_bus_clk.common.clk, + &dpu_spi_aclk.common.clk, + &v2d_clk.common.clk, + &emmc_bus_clk.common.clk, +}; +#endif + +static int clk_k1_enable(struct clk *clk) +{ + const struct spacemit_ccu_data *data; + struct clk *c; + struct clk *pclk; + int ret, i; + + data = (struct spacemit_ccu_data *)dev_get_driver_data(clk->dev); + for (i = 0; i < data->num; i++) { + if (clk->id == data->clks[i]->id) { + c = data->clks[i]; + break; + } + } + if (i == data->num) + c = clk; + + pclk = clk_get_parent(c); + if (!IS_ERR_OR_NULL(pclk)) { + ret = ccf_clk_enable(pclk); + if (ret) + return ret; + } + ret = ccu_gate_enable(c); + return ret; +} + +static int clk_k1_disable(struct clk *clk) +{ + const struct spacemit_ccu_data *data; + struct clk *c; + struct clk *pclk; + int ret, i; + + data = (struct spacemit_ccu_data *)dev_get_driver_data(clk->dev); + for (i = 0; i < data->num; i++) { + if (clk->id == data->clks[i]->id) { + c = data->clks[i]; + break; + } + } + if (i == data->num) + c = clk; + + pclk = clk_get_parent(c); + if (!IS_ERR_OR_NULL(pclk)) { + ret = ccf_clk_disable(pclk); + if (ret) + return ret; + } + ret = ccu_gate_disable(c); + return ret; +} + +#define K1_CLK_OPS(name) \ +static const struct clk_ops k1_##name##_clk_ops = { \ + .set_rate = ccf_clk_set_rate, \ + .get_rate = ccf_clk_get_rate, \ + .enable = clk_k1_enable, \ + .disable = clk_k1_disable, \ + .set_parent = ccf_clk_set_parent, \ + .of_xlate = k1_##name##_clk_of_xlate, \ +} + +static const struct spacemit_ccu_data k1_ccu_apmu_data = { + .clks = k1_ccu_apmu_clks, + .num = ARRAY_SIZE(k1_ccu_apmu_clks), + .offset = K1_APMU_ID, +}; + +/* + * The K1 clock providers are split across separate syscon devices (PLL, + * MPMU, APMU, APBC) whose clocks parent across device boundaries. U-Boot's + * CCF resolves a parent by name at registration time and silently binds an + * orphan if the parent is not registered yet, so a controller must not + * register its clocks before the controllers owning its parents have probed. + * Force the parent controller to probe by its driver: this is robust against + * device tree node/phandle ordering and needs no helper "clocks" wiring in + * the syscon nodes. + */ +static int k1_clk_force_probe(const struct driver *drv) +{ + struct udevice *dev; + + return uclass_get_device_by_driver(UCLASS_CLK, drv, &dev); +} + +static int k1_clk_register(struct udevice *dev, struct regmap *regmap, + struct regmap *lock_regmap, + const struct spacemit_ccu_data *data) +{ + int i, ret; + + for (i = 0; i < data->num; i++) { + struct clk *clk = data->clks[i]; + struct ccu_common *common; + + if (!clk) + continue; + + common = clk_to_ccu_common(clk); + common->regmap = regmap; + common->lock_regmap = lock_regmap; + + if (common->clk.id < K1_PLL_ID) + clk->id = common->clk.id + data->offset; + else + clk->id = common->clk.id; + + ret = common->init(common); + if (ret) + return ret; + } + + return 0; +} + +static int k1_pll_clk_probe(struct udevice *dev) +{ + struct regmap *base_regmap, *lock_regmap = NULL; + const struct spacemit_ccu_data *data; + int ret; + + clk_register_fixed_rate(NULL, "clock-dummy", 0); + + ret = regmap_init_mem(dev_ofnode(dev), &base_regmap); + if (ret) + return ret; + + /* + * The lock status of PLLs locate in MPMU region, while PLLs themselves + * are in APBS region. Reference to MPMU syscon is required to check PLL + * status. + */ + if (device_is_compatible(dev, "spacemit,k1-pll")) { + struct ofnode_phandle_args mpmu_args; + + ret = dev_read_phandle_with_args(dev, "spacemit,mpmu", NULL, 0, 0, + &mpmu_args); + if (ret) + return ret; + + ret = regmap_init_mem(mpmu_args.node, &lock_regmap); + if (ret) + return ret; + } + + data = (struct spacemit_ccu_data *)dev_get_driver_data(dev); + + ret = k1_clk_register(dev, base_regmap, lock_regmap, data); + if (ret) + return -EPROBE_DEFER; + + return 0; +} + +static int k1_mpmu_clk_probe(struct udevice *dev) +{ + struct regmap *base_regmap, *lock_regmap = NULL; + const struct spacemit_ccu_data *data; + int ret; + + /* MPMU clocks parent off the PLL controller */ + ret = k1_clk_force_probe(DM_DRIVER_GET(k1_pll_clk)); + if (ret) + return ret; + + ret = regmap_init_mem(dev_ofnode(dev), &base_regmap); + if (ret) + return ret; + + data = (struct spacemit_ccu_data *)dev_get_driver_data(dev); + + ret = k1_clk_register(dev, base_regmap, lock_regmap, data); + if (ret) + return -EPROBE_DEFER; + + return 0; +} + +static int k1_apmu_clk_probe(struct udevice *dev) +{ + struct regmap *base_regmap, *lock_regmap = NULL; + const struct spacemit_ccu_data *data; + int ret; + + /* APMU clocks parent off the PLL and MPMU controllers */ + ret = k1_clk_force_probe(DM_DRIVER_GET(k1_pll_clk)); + if (ret) + return ret; + ret = k1_clk_force_probe(DM_DRIVER_GET(k1_mpmu_clk)); + if (ret) + return ret; + + ret = regmap_init_mem(dev_ofnode(dev), &base_regmap); + if (ret) + return ret; + + data = (struct spacemit_ccu_data *)dev_get_driver_data(dev); + + ret = k1_clk_register(dev, base_regmap, lock_regmap, data); + if (ret) + return -EPROBE_DEFER; + + return 0; +} + +static int k1_apbc_clk_probe(struct udevice *dev) +{ + struct regmap *base_regmap, *lock_regmap = NULL; + const struct spacemit_ccu_data *data; + int ret; + + /* APBC clocks parent off the PLL, MPMU and APMU controllers */ + ret = k1_clk_force_probe(DM_DRIVER_GET(k1_pll_clk)); + if (ret) + return ret; + ret = k1_clk_force_probe(DM_DRIVER_GET(k1_mpmu_clk)); + if (ret) + return ret; + ret = k1_clk_force_probe(DM_DRIVER_GET(k1_apmu_clk)); + if (ret) + return ret; + + ret = regmap_init_mem(dev_ofnode(dev), &base_regmap); + if (ret) + return ret; + + data = (struct spacemit_ccu_data *)dev_get_driver_data(dev); + + ret = k1_clk_register(dev, base_regmap, lock_regmap, data); + if (ret) + return -EPROBE_DEFER; + + return 0; +} + +static int k1_pll_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) +{ + if (args->args_count > 1) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + if (args->args_count) + clk->id = K1_PLL_ID + args->args[0]; + else + clk->id = K1_PLL_ID; + + return 0; +} + +static int k1_mpmu_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) +{ + if (args->args_count > 1) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + if (args->args_count) + clk->id = K1_MPMU_ID + args->args[0]; + else + clk->id = K1_MPMU_ID; + + return 0; +} + +static int k1_apbc_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) +{ + if (args->args_count > 1) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + if (args->args_count) + clk->id = K1_APBC_ID + args->args[0]; + else + clk->id = K1_APBC_ID; + + return 0; +} + +static int k1_apmu_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) +{ + if (args->args_count > 1) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + if (args->args_count) + clk->id = K1_APMU_ID + args->args[0]; + else + clk->id = K1_APMU_ID; + + return 0; +} + +static const struct udevice_id k1_pll_clk_match[] = { + { .compatible = "spacemit,k1-pll", + .data = (ulong)&k1_ccu_pll_data }, + { /* sentinel */ }, +}; + +K1_CLK_OPS(pll); + +U_BOOT_DRIVER(k1_pll_clk) = { + .name = "k1_pll_clk", + .id = UCLASS_CLK, + .of_match = k1_pll_clk_match, + .probe = k1_pll_clk_probe, + .ops = &k1_pll_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +static int k1_mpmu_clk_bind(struct udevice *dev) +{ + return spacemit_k1_reset_bind(dev, SPACEMIT_K1_RESET_MPMU); +} + +static int k1_apbc_clk_bind(struct udevice *dev) +{ + return spacemit_k1_reset_bind(dev, SPACEMIT_K1_RESET_APBC); +} + +static int k1_apmu_clk_bind(struct udevice *dev) +{ + return spacemit_k1_reset_bind(dev, SPACEMIT_K1_RESET_APMU); +} + +static int k1_apbc2_clk_bind(struct udevice *dev) +{ + return spacemit_k1_reset_bind(dev, SPACEMIT_K1_RESET_APBC2); +} + +static const struct udevice_id k1_mpmu_clk_match[] = { + { .compatible = "spacemit,k1-syscon-mpmu", + .data = (ulong)&k1_ccu_mpmu_data }, + { /* sentinel */ }, +}; + +K1_CLK_OPS(mpmu); + +U_BOOT_DRIVER(k1_mpmu_clk) = { + .name = "k1_mpmu_clk", + .id = UCLASS_CLK, + .of_match = k1_mpmu_clk_match, + .bind = k1_mpmu_clk_bind, + .probe = k1_mpmu_clk_probe, + .ops = &k1_mpmu_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +static const struct udevice_id k1_apbc_clk_match[] = { + { .compatible = "spacemit,k1-syscon-apbc", + .data = (ulong)&k1_ccu_apbc_data }, + { /* sentinel */ }, +}; + +K1_CLK_OPS(apbc); + +U_BOOT_DRIVER(k1_apbc_clk) = { + .name = "k1_apbc_clk", + .id = UCLASS_CLK, + .of_match = k1_apbc_clk_match, + .bind = k1_apbc_clk_bind, + .probe = k1_apbc_clk_probe, + .ops = &k1_apbc_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +static const struct udevice_id k1_apmu_clk_match[] = { + { .compatible = "spacemit,k1-syscon-apmu", + .data = (ulong)&k1_ccu_apmu_data }, + { /* sentinel */ }, +}; + +K1_CLK_OPS(apmu); + +U_BOOT_DRIVER(k1_apmu_clk) = { + .name = "k1_apmu_clk", + .id = UCLASS_CLK, + .of_match = k1_apmu_clk_match, + .bind = k1_apmu_clk_bind, + .probe = k1_apmu_clk_probe, + .ops = &k1_apmu_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +static const struct udevice_id k1_apbc2_clk_match[] = { + { .compatible = "spacemit,k1-syscon-apbc2" }, + { /* sentinel */ }, +}; + +U_BOOT_DRIVER(k1_apbc2_clk) = { + .name = "k1_apbc2_clk", + .id = UCLASS_CLK, + .of_match = k1_apbc2_clk_match, + .bind = k1_apbc2_clk_bind, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/spacemit/clk_common.h b/drivers/clk/spacemit/clk_common.h new file mode 100644 index 00000000000..ea5ebf57784 --- /dev/null +++ b/drivers/clk/spacemit/clk_common.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 SpacemiT Technology Co. Ltd + * Copyright (c) 2024-2025 Haylen Chu <[email protected]> + * Copyright (c) 2025 Junhui Liu <[email protected]> + * Copyright (c) 2025-2026 RISCstar Ltd. + * + * Authors: Haylen Chu <[email protected]> + */ + +#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_ */ diff --git a/drivers/clk/spacemit/clk_ddn.c b/drivers/clk/spacemit/clk_ddn.c new file mode 100644 index 00000000000..7b93f30d5c3 --- /dev/null +++ b/drivers/clk/spacemit/clk_ddn.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 SpacemiT Technology Co. Ltd + * Copyright (c) 2024-2025 Haylen Chu <[email protected]> + * Copyright (c) 2025 Junhui Liu <[email protected]> + * Authors: Haylen Chu <[email protected]> + * + * 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, +}; diff --git a/drivers/clk/spacemit/clk_ddn.h b/drivers/clk/spacemit/clk_ddn.h new file mode 100644 index 00000000000..1330ced23b1 --- /dev/null +++ b/drivers/clk/spacemit/clk_ddn.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 SpacemiT Technology Co. Ltd + * Copyright (c) 2024-2025 Haylen Chu <[email protected]> + * Copyright (c) 2025 Junhui Liu <[email protected]> + * Copyright (c) 2025-2026 RISCstar Ltd. + * + * Authors: Haylen Chu <[email protected]> + */ + +#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_ */ diff --git a/drivers/clk/spacemit/clk_mix.c b/drivers/clk/spacemit/clk_mix.c new file mode 100644 index 00000000000..a1158512a92 --- /dev/null +++ b/drivers/clk/spacemit/clk_mix.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 SpacemiT Technology Co. Ltd + * Copyright (c) 2024-2025 Haylen Chu <[email protected]> + * Copyright (c) 2025 Junhui Liu <[email protected]> + * Authors: Haylen Chu <[email protected]> + * + * 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, +}; diff --git a/drivers/clk/spacemit/clk_mix.h b/drivers/clk/spacemit/clk_mix.h new file mode 100644 index 00000000000..26a12cedd0d --- /dev/null +++ b/drivers/clk/spacemit/clk_mix.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 SpacemiT Technology Co. Ltd + * Copyright (c) 2024-2025 Haylen Chu <[email protected]> + * Copyright (c) 2025 Junhui Liu <[email protected]> + * Copyright (c) 2025-2026 RISCstar Ltd. + * + * Authors: Haylen Chu <[email protected]> + */ + +#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_ */ diff --git a/drivers/clk/spacemit/clk_pll.c b/drivers/clk/spacemit/clk_pll.c new file mode 100644 index 00000000000..56da70af58a --- /dev/null +++ b/drivers/clk/spacemit/clk_pll.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 SpacemiT Technology Co. Ltd + * Copyright (c) 2024-2025 Haylen Chu <[email protected]> + * Copyright (c) 2025 Junhui Liu <[email protected]> + * Authors: Haylen Chu <[email protected]> + */ + +#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, +}; diff --git a/drivers/clk/spacemit/clk_pll.h b/drivers/clk/spacemit/clk_pll.h new file mode 100644 index 00000000000..3987cc1141b --- /dev/null +++ b/drivers/clk/spacemit/clk_pll.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 SpacemiT Technology Co. Ltd + * Copyright (c) 2024-2025 Haylen Chu <[email protected]> + * Copyright (c) 2025 Junhui Liu <[email protected]> + * Copyright (c) 2025-2026 RISCstar Ltd. + * + * Authors: Haylen Chu <[email protected]> + */ + +#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_ */ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 75b35fbc5be..a11f3043670 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -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 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 863557e45ce..020e23c7252 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -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 diff --git a/drivers/gpio/spacemit_gpio.c b/drivers/gpio/spacemit_gpio.c new file mode 100644 index 00000000000..de71880df15 --- /dev/null +++ b/drivers/gpio/spacemit_gpio.c @@ -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, +}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 5f6586c78ba..0e8b2408376 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -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 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 2da649e97d3..0e3a0f9fb5f 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -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 diff --git a/drivers/i2c/k1_i2c.c b/drivers/i2c/k1_i2c.c new file mode 100644 index 00000000000..a08ff15803d --- /dev/null +++ b/drivers/i2c/k1_i2c.c @@ -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, +}; diff --git a/drivers/i2c/k1_i2c.h b/drivers/i2c/k1_i2c.h new file mode 100644 index 00000000000..856947aa43b --- /dev/null +++ b/drivers/i2c/k1_i2c.h @@ -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 diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 44e67cd913a..9a96be11e6d 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -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 diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 7100b64bf22..dee03662d45 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -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); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 69ee9155a01..fd30aaeeaa8 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -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" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 6c6e8b59122..91149796bb5 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -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 diff --git a/drivers/pinctrl/spacemit/Kconfig b/drivers/pinctrl/spacemit/Kconfig new file mode 100644 index 00000000000..6aab89e160c --- /dev/null +++ b/drivers/pinctrl/spacemit/Kconfig @@ -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. diff --git a/drivers/pinctrl/spacemit/Makefile b/drivers/pinctrl/spacemit/Makefile new file mode 100644 index 00000000000..0dc43c72cb1 --- /dev/null +++ b/drivers/pinctrl/spacemit/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-y += pinctrl-k1.o diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c new file mode 100644 index 00000000000..a6a22eacac7 --- /dev/null +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Yixun Lan <[email protected]> + * 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, +}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 4bd9b4e1940..d45869cb30f 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -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 diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 2cda5a892fd..0ac92084221 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -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 diff --git a/drivers/power/pmic/pmic_spacemit_p1.c b/drivers/power/pmic/pmic_spacemit_p1.c new file mode 100644 index 00000000000..46c5926874c --- /dev/null +++ b/drivers/power/pmic/pmic_spacemit_p1.c @@ -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, +}; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 9f80bb953e8..c3dc5d6f97b 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -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. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index b8599ea7fd9..3a148545387 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -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 diff --git a/drivers/power/regulator/spacemit_p1_regulator.c b/drivers/power/regulator/spacemit_p1_regulator.c new file mode 100644 index 00000000000..7b5eb710edf --- /dev/null +++ b/drivers/power/regulator/spacemit_p1_regulator.c @@ -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, +}; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index c851354c7a5..db764ed5ace 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -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 diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 3fce96509cd..384f4336331 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -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/ diff --git a/drivers/reset/reset-spacemit-k1.c b/drivers/reset/reset-spacemit-k1.c deleted file mode 100644 index 613e002fc4f..00000000000 --- a/drivers/reset/reset-spacemit-k1.c +++ /dev/null @@ -1,548 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2022 Spacemit Inc. - * Copyright (C) 2025 Huan Zhou <[email protected]> - */ - -#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), -}; diff --git a/drivers/reset/spacemit/Makefile b/drivers/reset/spacemit/Makefile new file mode 100644 index 00000000000..16722c68954 --- /dev/null +++ b/drivers/reset/spacemit/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# SpacemiT reset drivers + +obj-y += reset-spacemit-k1.o diff --git a/drivers/reset/spacemit/reset-spacemit-k1.c b/drivers/reset/spacemit/reset-spacemit-k1.c new file mode 100644 index 00000000000..129e1cc96f1 --- /dev/null +++ b/drivers/reset/spacemit/reset-spacemit-k1.c @@ -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 <[email protected]> + * 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; +} diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 65ab3e306d7..5692510a558 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -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, }, { } }; diff --git a/include/configs/bananapi-f3.h b/include/configs/bananapi-f3.h deleted file mode 100644 index 97cf4d72df0..00000000000 --- a/include/configs/bananapi-f3.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2024, Kongyang Liu <[email protected]> - * - */ - -#ifndef __CONFIG_H -#define __CONFIG_H - -#define CFG_SYS_SDRAM_BASE 0x0 -#define CFG_SYS_NS16550_IER 0x40 /* UART Unit Enable */ - -#endif /* __CONFIG_H */ diff --git a/include/configs/k1.h b/include/configs/k1.h new file mode 100644 index 00000000000..e09c3594905 --- /dev/null +++ b/include/configs/k1.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2024, Kongyang Liu <[email protected]> + * 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 */ diff --git a/include/dt-bindings/reset/spacemit-k1-reset.h b/include/dt-bindings/reset/spacemit-k1-reset.h deleted file mode 100644 index 74db58b27ef..00000000000 --- a/include/dt-bindings/reset/spacemit-k1-reset.h +++ /dev/null @@ -1,118 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2022 Spacemit Inc. - * Copyright (C) 2025 Huan Zhou <[email protected]> - */ - -#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 diff --git a/include/power/spacemit_p1.h b/include/power/spacemit_p1.h new file mode 100644 index 00000000000..ef5a45e8cec --- /dev/null +++ b/include/power/spacemit_p1.h @@ -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_ */ diff --git a/include/soc/spacemit/k1-reset.h b/include/soc/spacemit/k1-reset.h new file mode 100644 index 00000000000..1dbfd6b1cb0 --- /dev/null +++ b/include/soc/spacemit/k1-reset.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 */ diff --git a/include/soc/spacemit/k1-syscon.h b/include/soc/spacemit/k1-syscon.h new file mode 100644 index 00000000000..331cc1d35bb --- /dev/null +++ b/include/soc/spacemit/k1-syscon.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__ */ |
